How do I use HttpWebRequest GET method w/ ContentT

2019-02-18 18:55发布

问题:

This one is real simple, run this Silverlight4 example with the ContentType property commented out and you'll get back a response from from my service in xml. Now uncomment the property and run it and you'll get an ProtocolViolationException, what should happen is the service returns JSON formatted data.

#if DEBUG
                Address = "http://localhost.:55437/Services/GetFoodDescriptionsLookup(2100)";
#else
                Address = "http://stephenpattenconsulting.com/Services/GetFoodDescriptionsLookup(2100)";
#endif

Uri httpSite = new Uri(Address);

HttpWebRequest wreq = 
(HttpWebRequest)WebRequestCreator.ClientHttp.Create(httpSite);

//wreq.ContentType = "application/json"; // Wrong
wreq.Accept = "application/json"; // Right

wreq.BeginGetResponse((cb) =>
{
    HttpWebRequest rq = cb.AsyncState as HttpWebRequest;
    HttpWebResponse resp = rq.EndGetResponse(cb) as HttpWebResponse; // Exception
    StreamReader rdr = new StreamReader(resp.GetResponseStream());
    string result =  rdr.ReadToEnd(); 
    rdr.Close();
}, wreq);

New exception

System.NotSupportedException was unhandled by user code Message="" StackTrace: at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state) at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult) at com.patten.silverlight.ViewModels.WebRequestLiteViewModel.b_0(IAsyncResult cb) at System.Net.Browser.BrowserHttpWebRequest.<>c_DisplayClassd.b__b(Object state2) at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading.ThreadPoolWaitCallback.PerformWaitCallback() InnerException: System.NotSupportedException Message=Specified method is not supported. StackTrace: at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult) at System.Net.Browser.BrowserHttpWebRequest.<>c_DisplayClass5.b_4(Object sendState) at System.Net.Browser.AsyncHelper.<>c_DisplayClass2.b__0(Object sendState) InnerException:

WORKING NOW

The exception that I was getting was due to a hack I use to get Fiddler to show the loop-back adapter i.e. http://localhost.:55437/Services/GetFoodDescriptionsLookup(2100), notice the extra DOT after the word localhost.

That was it!

I know what everyone is thinking, this could have been solved it I would have included some of these details.. like the debug flag that changes the URI, I removed most of the code to make it easier to read on SO, thinking that the problem was in the networking stack. Hard lesson(s) learned.

Thanks to everyone who took the time to get involved with this, both on stackoverflow and offline.

For completeness I have added the jQuery function that had me thinking this whole time I needed to be setting "content-type" instead of "accept" when in the SL4 application. (READ THE DOCS!)

function CallService(serviceUrl, data, callback) {
    $.ajax({
        url: serviceUrl,
        data: data,
        dataType: 'json',
        contextType: "application/json", 
        cache: false,
        success: callback,
        error: function (msg) {
            alert("Request Failed!");
        }
    });
}

回答1:

Stephen, setting the content type is to allow SENDING JSON, not receiving it. There are several ways to do this. You can support both JSON and XML or only JSON. If all you want to do is support JSON then you can do this by setting the ResponseFormat property on the WebGet attribute for the service operation.

If you want to support both JSON and XML then you need to either enable automatic format selection on the service and send an accept header of "application/json", otherwise you can use a format string parameter in the query string and have the service use the WebOperationContext.OutgoingResponse.Format property.

Here's a post that covers various ways to do this from the server side. If you go the route of supporting automatic format selection then you need to set the accept header of the HttpWebRequest to "application/json"



回答2:

If you finally determine that it's a problem in the Silverlight HttpWebRequest, i.e., it might have a bug or excessively stupid security check in it that keeps it from handling text/json responses, one reasonable (if annoying) workaround might be to shell out to javascript to make the request, using the XMLHttpRequest object, and then return the results to Silverlight. Annoying, but possible.



回答3:

Glenn Block's answer already seems to say this but I guess it was not clear enough.

The Content-Type header tells what type of data is in a POST body or HTTP response body

Your HTTP request is not either of these. It is a GET request without any body. This is why you are getting a protocol violation exception - GET requests cannot have a Content-Type header because by definition they do not have any content.

Your Fiddler request is working simply because WCF is very accomodating to buggy clients. The error is coming from inside the Silverlight networking stack, which refuses to send a broken request.

Let's take it from the top: you want to get back JSON data. This is accomplished using the Accept header, which tells the server what kind of data you want to get back. Therefore, replace this

wreq.ContentType = "application/json"; 

with this

wreq.Accept = "application/json"; 

and you will get JSON data from the server.



回答4:

Can you post the details for the WCF End point? If it is returning XAML, the only way to get JSON is if it is configured to. The initial request simply requests a URL, it is not specifying a "mode" so I'm assuming the WCF is defaulting to XML. There should either be a specific endpoint to go to for JSON responses, or a setting on the WCF to return those only.