I'm using the HttpClient class to communicate to a web service in my WPF application.
When I make consecutive GET requests on the same connection, everything works fine. However, when I make consecutive PUT/PATCH requests on the same connection, the first request executes accurately and I receive a response but the second request does not include the body in the request and I receive the infamous error of "The server committed a protocol violation. Section=ResponseStatusLine".
My requests do complete successfully if I manually close the connection after every request by adding Connection: close to the header. This "solution" is a bad pattern and performance will not scale appropriately.
Below is a debranded version of a list of my TCP Stream Output from the requests being sent:
Wireshark: Follow TCP Stream Output
GET /domain/api/tenant/current/object?objectName=Lizbot HTTP/1.1
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 50
{"Data":[{"Id":123,"ObjectName":"Lizbot","Date":null}],"Errors":[]}
PATCH /domain/api/tenant/current/object/123 HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
Content-Length: 50
{"Id":123,"ObjectName":"Lizbot","Date":null}
HTTP/1.1 204 No Content
Content-Type: application/json; charset=utf-8
{"Data":null,"Errors":[]}
PATCH /domain/api/tenant/current/object/123/otherObject HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
HTTP/1.1 400 Bad Request</b>
Content-Type: text/html; charset=us-ascii
Connection: close
Content-Length: 311
Notice that the second PATCH is missing the object it's supposed to patch with. If I change the order of the PATCHing, the second PATCH is still missing its object.
This error appears to be common with a few known solutions which I have tried. They consist of this solution which involves setting the useUnsafeHeaderParsing property to TRUE and setting the Keep-Alive property to FALSE in the Web.Config. I also tried the solution of setting these properties in this manner shown below:
ServicePointManager.DefaultConnectionLimit = 2;
ServicePointManager.Expect100Continue = false;
None of these solutions worked. It should be noted that when using the Http Debugging proxy tool, Fiddler, to capture these requests, I don't receive any errors.
So what I am asking for is if anyone knows a good solution to alleviate this error so I can make multiple requests in a connection without losing the body of an update. If more details are needed, I am happy to supply them.
After much debugging and reading, I realized I was trying to edit the Web.Config file of the WPF application instead of the app.config file!
So if you drop this code in the app.config file at the root of the configuration tag for a WPF application, it fixes the problem.
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing = "true"/>
</settings>
</system.net>
The underlying problem is that the PATCH response includes content within the body of the response. Ensure that the server does not send content when sending a 204 No Content.
Related
I'm successfully using the Graph API for a variety of things but I need to access to the OneNote API to perform student and teacher add/remove operations on Class Notebooks. When I request a token the same way that I do for Graph with the https://www.onenote.com resource it provides one but when I try to use it to access the OneNote API no matter what (valid) request I send I get 401 - "The request does not contain a valid authentication token."
I've tried using the v1.0 endpoint to generate a token instead with the same results.
My token requests:
POST https://login.microsoftonline.com/{my tenant}/oauth2/v2.0/token HTTP/1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Host: login.microsoftonline.com
Content-Length: 213
Expect: 100-continue
Connection: Keep-Alive
grant_type=client_credentials&client_id={my appid}&client_secret={my secret}&tenant={my tenant}&scope=https%3A%2F%2Fwww.onenote.com%2F.default
OR
POST https://login.microsoftonline.com/{my tenant}/oauth2/token HTTP/1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Host: login.microsoftonline.com
Content-Length: 161
Expect: 100-continue
grant_type=client_credentials&client_id={my appid}&client_secret={my secret}&resource=https%3A%2F%2Fwww.onenote.com
Both return something containing an access_token, like:
{"token_type":"Bearer","expires_in":"3600","ext_expires_in":"3600","expires_on":"1543513719","not_before":"1543509819","resource":"https://www.onenote.com","access_token":"{a token}"}
Request:
GET https://www.onenote.com/api/v1.0/myorganization/groups/{group id}/notes/ HTTP/1.1
ContentType: application/json
Authorization: Bearer {token returned from /token request}
Cache-Control: no-store, no-cache
Host: www.onenote.com
Response:
code=40001
message=The request does not contain a valid authentication token.
For reference, this question is basically a follow-up to: Adding Students with the API and Class Notebook
You're on the right track.
Resource is the right way with the 1.0 auth endpoint.
Scopes need to be registered on the app portal, so you'll need to go back in and add OneNote scopes in the portal.
I'm not 100% sure, but IIRC the resource for onenote might require a trailing '/'.
I am hosting an ASP.NET Web Api on WinSrv 2012 R2 and IIS 8.5.
The web api has one action in a controller that receives a json object - for the sake of discussion it looks like this:
[HttpPost, "api/action"]
ResponseModel GetIt(RequestModel requestData)
It works great except when I try to post data that's large. For example, when I try to post 8Mb of json (it includes base64 encoded image files).
How it fails is rather interesting. Basically, the action is called but model (RequestModel) is missing certain key properties. Leading me to believe that there's an error deserializing the object but I dont' see that. My Web API action immediately throws an exception because it's getting a few key null values.
I turned on FailedRequestTracking in IIS and I see the following:
The request comes in with these headers:
Cache-Control: no-store, no-cache
Connection: Keep-Alive
Content-Length: 12262199
Content-Type: application/json; charset=utf-8
Accept: application/json
Expect: 100-continue
In this case the object is ~12MB. The various handler matching starts and eventually IIS settles on the ManagedPipelineHandler to handle the request. It reads 16,384 bytes, then reads 16,384 bytes again, then reads 16,384 bytes one final time and you see the NOTIFY_MODULE_COMPLETION entry.
I see the AspNetHttpHandlerEnter event and after setting up the response header I see the 500 error from my exception. MODULE_SET_RESPONSE_ERROR_STATUS
Does anyone know why the ManagedPipelineHandler is only reading 48K? I need to be able to post up to 25Mb of data.
I create a web services and a ConsoleApplication to consume it. I am using fiddler to see the traffic, i was hoping that the request and response was SOAP but that's don't happend, only the request was SOAP, not the response.
How can i force to my service that respond using soap?
This is the raw http header obtained:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 17 Nov 2014 20:19:22 GMT
Content-Length: 311
The respond is in binary.
My web-service was created adding a Web Services item to ASP.NET Empty Web Application project, and i don't modified, later I published in IIS.
My consumer is very simple too. I just add the service reference and create an instance.
var binding = new BasicHttpBinding();
var endpointAddress = new EndpointAddress("http://172.20.48.59/web-services/MyWebService.asmx");
var serviceRefWithoutConfig = new ServiceReference1.MyWebServiceSoapClient(binding, endpointAddress);
Console.WriteLine(serviceRefWithoutConfig.HelloWorld());
The HTTP response in question is compressed with GZIP.
What happens after you click the big yellow bar in Fiddler that says Response is encoded and may require decoding before inspection. Click here to transform.?
If you are creating something new, I strongly suggest you look at using WebAPI for something like this.
It's much easier to control the output of your methods, and the resulting service will be easier to consume from mobile platforms, should the need arise in future.
I have code that works, but I'm having a difficult time making the connection as to why it works.
I have code in an AngularJS factory function that makes this call:
$http.get('http://webServerName/PrestoWebApi/api/apps/')
And this is the Web API controller (C#):
[EnableCors(origins: "http://webServerName", headers: "*", methods: "*")]
public class AppsController : ApiController
The source of the call would be a user's computer, for example, a laptop with the name JoesLaptop. And that laptop could run anywhere. (Currently, this is all running inside one LAN, but the user could be anywhere.)
So why does specifying the web server name within the EnableCors attribute work? Isn't the request coming from the browser on Joe's laptop and not from the web server itself?
Edit
If I remove the EnableCors attribute, I get this error in the F12 tools in the browser:
XMLHttpRequest cannot load http://webServerName/PrestoWebApi/api/apps/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://webServerName' is therefore not allowed access.
Edit 2
Request:
GET http://fs-6103.fs.local/PrestoWebApi/api/apps/ HTTP/1.1
Host: fs-6103.fs.local
Connection: keep-alive
Accept: application/json, text/plain, */*
Origin: http://fs-6103
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
Referer: http://fs-6103/PrestoWebApi/app/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Response:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
Access-Control-Allow-Origin: http://fs-6103
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 31 Oct 2014 18:30:05 GMT
Content-Length: 2931171
If webServerName is rendering an HTML page that is starting an asynchronous request to webServerName, then CORS doesn't apply and your server will serve that resource anyway.
I'm pretty sure that must be your case.
UPDATE
Based on the latest edits of the question and the comments that the OP has made bellow this answer, this is what must be happening.
The HTTP server that it's serving both the main HTML page and the API resource is the same, therefore there shouldn't be any need to EnableCORS. However, according to the headers of the Request the page is being served from http://fs-6103 and the $http.get is made to http://fs-6103.fs.local. That should explain everything.
UPDATE 2
Ok, I'm willing to bet that this is what's happening here:
The main page is being served by http://fs-6103
The $http.get is made towards: http://fs-6103.fs.local/
So far I'm not speculating, this is what the request is saying
The OP must have [EnableCors(origins: "http://fs-6103", headers: "*", methods: "*")] set into the API controller.
When this is disabled the OP is getting the error: No 'Access-Control-Allow-Origin' header is present on the requested resource, as it should be expected. And when the OP enables it everything works as expected.
Browsers/clients handle the security, and generally restrict things to single origin, meaning they only accept stuff from the server they made the request to. Enabling cors in the header (ACAO) or wherever lets that server tell the browser, "hey those other Cross origin resources are with me." The browser will generally go along with that.
I'm using RestSharp to build a Rest access to my MVC entry points (actually so I can use them from monotouch, but right now I'm testing on Windows 7, vs2010, .net 4, RestSharp 104.1)
if I create a request and call
client.ExecuteAsPost<Model.Client>( request );
it works, I can see in fiddler the raw packet
POST http://localhost.:49165/Services/Client/ClientAdminService/FindClient HTTP/1.1
Timestamp: Monday, March 18, 2013 1:56:02 AM
X-PS-Authentication: YADAYADA:<deleted for brevity>==
Accept: application/xml
User-Agent: RestSharp 104.1.0.0
Content-Type: application/xml; charset=utf-8
Host: localhost.:49165
Content-Length: 256
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<Client xmlns="http://schemas.datacontract.org/2004/07/PSRMWebService.Model.Version1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ID>0</ID><MailingAddress i:nil="true"/><Mask>Name</Mask><Name>Rykercom</Name><PhysicalAddress i:nil="true"/></Client>
as you can see at the end is the serialized data blob I need to send to the server (of type Model.Client) this is added to the request using
Request.AddParameter("application/xml; charset=utf-8", DataPacket, RestSharp.ParameterType.RequestBody);
where DataPacket is the serialized blob created using a DataContractSerializer
Now if I change the code to call
Client.ExecuteAsyncPost<Model.Client>(Request, (response, handle) => { OnFindClientAsyncComplete(response, handle, Callback ); }, "POST");
Using Fiddler I get quite a different packet with no Body, no content type, and therefore a failed response from the server.
POST http://localhost.:49165/Services/Client/ClientAdminService/FindClient HTTP/1.1
Timestamp: Monday, March 18, 2013 2:35:08 AM
X-PS-Authentication: YADAYADA:<deleted for bevity>==
Accept: application/xml
User-Agent: RestSharp 104.1.0.0
Host: localhost.:49165
Content-Length: 0
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
the X-PS-Authentication is just a custom auth string. Anyone any ideas why the async call is leaving me with an empty message body ?
The simple answer is clone the current github repository for rest sharp and build it yourself. It appears the fix for executeasync is already in the tree.
Any chance who ever own the Nuget package can update it to the lastest sources ?
Thanks