title: ASP.NET MVC Core 3.0 - Why API Request from body keeps returning !ModelState.IsValid?
body: <p>I'm currently using <code>ASP.NET MVC Core 3.0</code> to create an API project. I was successful to send a POST request without parameter. But currently I'm having a problem when trying to send a POST request with the parameter in JSON via Postman, always getting invalid request as shown below.</p>
  
  <p><a href="https://i.stack.imgur.com/ntD98.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ntD98.png" alt="enter image description here"></a></p>
  
  <p>Notice that there's also <code>key</code> param in the query string to authorize the request using the middleware I created. This part has no problem.</p>
  
  <p>Here's the code of the controller:
  </p>
  
  <pre><code>[ApiVersion("1.0")]
  [Route("api/v{version:apiVersion}/[action]")]
  [ApiController]
  public class ValuesController : ControllerBase
  {
      // POST api/values
      [HttpPost]
      public IActionResult Post([FromBody] UserRequest model)
      {
          if (!ModelState.IsValid)
              return BadRequest(new ApiResponse(400, "Model state is not valid."));
  
          return Ok($"Hello world, {model.Id}!");
      }
  }
  </code></pre>
  
  <p>The odd thing is, I've already created and used the class UserRequest as a parameter input, as shown below:
  </p>
  
  <pre><code>public class UserRequest
  {
      public string Id { get; set; }
  }
  </code></pre>
  
  <p>Here's my <code>Startup.cs</code> settings, I've already added <code>AddNewtonsoftJson</code> to enable JSON serializer input:
  </p>
  
  <pre><code>public void ConfigureServices(IServiceCollection services)
  {
      services.AddMvc(option =&gt; option.EnableEndpointRouting = false)
          .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
          .AddNewtonsoftJson(opt =&gt; opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
  
      /*Other API, DB settings and services goes here*/
      ...
  }
  </code></pre>
  
  <p>Here's my attempts so far:</p>
  
  <ol>
  <li>Added <code>[BindProperties]</code> on <code>UserRequest</code> class. Still returning same error.</li>
  <li>Removed <code>[FromBody]</code> on the parameter of controller. Still returning same error.</li>
  <li>Renamed <code>id</code> to <code>Id</code> to follow the naming inside <code>UserRequest</code> class. Still returning same error.</li>
  <li><p>Added this code on <code>Startup.cs</code>, this will execute <code>return BadRequest(new ApiResponse(400, "Model state is not valid."));</code>: 
  </p>
  
  <pre><code>.ConfigureApiBehaviorOptions(options =&gt;
  {
      options.SuppressModelStateInvalidFilter = true;
  })
  </code></pre></li>
  <li><p>Removed this code on <code>Startup.cs</code>
  </p>
  
  <pre><code>.AddNewtonsoftJson(opt =&gt; opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore)
  </code></pre>
  
  <p>It will return this instead:
  </p>
  
  <pre><code>{
      "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
      "title": "One or more validation errors occurred.",
      "status": 400,
      "traceId": "|f6037d12-44fa46ceaffd3dba.",
      "errors": {
          "$": [
              "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
          ]
      }
  }
  </code></pre></li>
  </ol>
  
  <p>Any help would be greatly appreciated.</p>
  
  <p><strong>Updated 12/11/2019: Here's how I handle the API key request:</strong>
  </p>
  
  <pre><code>public async Task Invoke(HttpContext httpContext, IApiKeyService apiKeyService)
  {
      var remoteIpAddress = httpContext.Connection.RemoteIpAddress;
  
      if (httpContext.Request.Path.StartsWithSegments("/api"))
      {
          _logger.LogInformation($"Request from {remoteIpAddress}.");
  
          var queryString = httpContext.Request.Query;
          queryString.TryGetValue("key", out var keyValue);
  
          if (keyValue.ToString().Any(char.IsWhiteSpace))
              keyValue = keyValue.ToString().Replace(" ", "+");
  
          if (httpContext.Request.Method != "POST")
          {
              httpContext.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
              await WriteJsonResponseAsync(httpContext, "Only POST method is allowed.");
              return;
          }
  
          if (keyValue.Count == 0)
          {
              httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
              await WriteJsonResponseAsync(httpContext, "API Key is missing.");
              return;
          }
  
          var isKeyValid = await apiKeyService.IsApiKeyValidAsync(keyValue);
          var isKeyActive = await apiKeyService.IsApiKeyActiveAsync(keyValue);
  
          if (!isKeyValid)
          {
              httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
              await WriteJsonResponseAsync(httpContext, "Invalid API Key.");
              return;
          }
  
          if (!isKeyActive)
          {
              httpContext.Response.StatusCode = StatusCodes.Status406NotAcceptable;
              await WriteJsonResponseAsync(httpContext, "Service is Deactivated.");
              return;
          }
      }
      await _next.Invoke(httpContext);
  }
  
  private static async Task WriteJsonResponseAsync(HttpContext httpContext, string message = null)
  {
      httpContext.Response.ContentType = "application/json";
      var response = new ApiResponse(httpContext.Response.StatusCode, message);
      var json = JsonConvert.SerializeObject(response);
      await httpContext.Response.WriteAsync(json);
  }
  </code></pre>