I'm facing a strange bug, where .NET Core 2.1 API seems to ignore a JSON body on certain cases.
- I advised many other questions (e.g
To be sure, the
Content-Type
header is set toapplication/json
. Also, FWIW, theHttpWebRequest
body is set as follows:using(var requestStream = httpWebRequest.GetRequestStream()) { JsonSerializer.Serialize(payload, requestStream); }
And called with:
var response = (HttpWebResponse)request.GetResponse();
Question
Why does
body
is null when used withHttpWebRequest
? Why does the JSON converter read methods are skipped in such cases?CodePudding user response:
The problem was in the underlying code of the serialization. So this line:
JsonSerializer.Serialize(payload, requestStream);
Was implemented using the default
UTF8
property:public void Serialize<T>(T instance, Stream stream) { using(var streamWriter = new StreamWriter(stream, Encoding.UTF8) // <-- Adds a BOM using(var jsonWriter = new JsonTextWriter(streamWriter)) { jsonSerializer.Serialize(jsonWriter, instance); // Newtonsoft.Json's JsonSerializer } }
The default
UTF8
property adds a BOM character, as noted in the documentation:It returns a UTF8Encoding object that provides a Unicode byte order mark (BOM). To instantiate a UTF8 encoding that doesn't provide a BOM, call any overload of the UTF8Encoding constructor.
It turns out that passing the BOM in a json is not allowed per the spec:
Implementations MUST NOT add a byte order mark (U FEFF) to the beginning of a networked-transmitted JSON text.
Hence .NET Core
[FromBody]
internal deserialization failed.Lastly, as for why the following did work (see demo here):
using (var reader = new StreamReader(context.Request.Body)) { var requestBody = await reader.ReadToEndAsync(); // works var parsed = JObject.Parse(requestBody); }
I'm not very sure. Certainly,
StreamReader
also usesUTF8
property by default (see remarks here), so it shouldn't remove the BOM, and indeed it doesn't. Per a test I did (see it here), it seems thatReadToEnd
is responsible for removing the BOM.For elaboration: