Home > Net >  ASP.NET Core 2.1 API POST body is null when called using HttpWebRequest, seems it can't be pars
ASP.NET Core 2.1 API POST body is null when called using HttpWebRequest, seems it can't be pars

Time:12-13

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 enter image description here

    To be sure, the Content-Type header is set to application/json. Also, FWIW, the HttpWebRequest 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 with HttpWebRequest? 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 uses UTF8 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 that ReadToEnd is responsible for removing the BOM.

    For elaboration:

  • Related