问题
I have a Web API project that has a few dozen RESTful methods, split about evenly between GETs, POSTs and PUTs. The system uses Entity Framework objects and Newtonsoft's JSON from Nuget (version 9.0.1).
Something I've done recently has suddenly broken all the POSTs and PUTs. I find that the [FromBody] objects I'm POST/PUTting arrive as null.
So my "Update User" method looks like so...
[HttpPut]
public IHttpActionResult Put([FromBody] User user)
...but "user" always arrives null. Likewise if I do this...
var obj = Request.Content.ReadAsAsync<object>().Result;
...then obj is null.
But if I do this...
var jsonString = Request.Content.ReadAsStringAsync().Result;
...then I get the expected JSON. (But for my architecture, I don't want the JSON, I want the object.)
From what I understand, this is the kind of behavior that I'd expect if something has already read the Request.Content. The Content stream is non-rewindable, and gets set to the last byte; but .ReadAsStringAsync() and .ReadAsByteArrayAsync() get around this by (I presume) copying the stream and handling it themselves.
I call this from a WPF application using HttpClient. Sample call...
using (HttpClient http = API.GetHttpClient())
{
string url = string.Format("{0}/User", thisApp.WebAPI_BaseUrl);
RILogManager.Default.SendString("url", url);
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter() ;
formatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
HttpResponseMessage response;
response = http.PutAsync<User>(url, user, formatter, "application/json").Result;
...
My "API.GetHttpClient()" routine looks like this. You can see I'm doing some JWT work on the client-side with a DelegateHandler, but I don't think that's pertinent here; it doesn't touch the outgoing request, just the incoming response.
public static HttpClient GetHttpClient(bool WithAuthToken = true)
{
App thisApp = (App)System.Windows.Application.Current;
//HttpClient http = new HttpClient();
HttpClient http = HttpClientFactory.Create(new JWTExpirationHandler());
http.BaseAddress = new Uri(thisApp.WebAPI_BaseUrl);
http.DefaultRequestHeaders.Accept.Clear();
http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if(WithAuthToken)
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", JWT);
return http;
}
I've confirmed that the incoming request is Content-type of "application/json" and UTF-8.
I have two DelegatingHandlers on the web server, but they don't seem to be the problem, because when I do the ReadAsAsync() at the very top of the first one, it's also null; and ReadAsStringAsync() returns the JSON.
So...what am I doing wrong? Again, this was working great, and something changed and all my POSTs and PUTs broke this way.
I saw some links that said to remove [Serializable] from my classes--but these are Entity Framework classes that I use in many places, and I don't want to do that.
Finally...when I call this Update PUT through Postman, it works.
UPDATE
Comparing the HttpRequests from my own client, and from Postman, I see that the former are "chunked" and the latter are not. This seems an important clue. I see some other folks dealing with the same:
ASP.NET Web Api - the framework is not converting JSON to object when using Chunked Transfer Encoding
I cannot find any clear indication of how to turn chunking off. I do see that there's a property to turn off chunking, but doing that doesn't help.
Question: What is triggering the "choice" to chunk? Is it the client choosing to do this, or the Controller, or some negotiation between the two?
回答1:
That was a long haul.
Don't know how it got there, but in my .csproj I had this:
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
<Private>True</Private>
</Reference>
Rather than this:
<Reference Include="System.Net.Http" />
And in my App.config I had this:
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
...rather than...not this. I didn't need this stuff. I just took it out.
来源:https://stackoverflow.com/questions/42195670/why-are-posts-and-puts-readasasync-null-but-readasstringasync-filled-aka