我目前正在使用 ASP.NET MVC Core 3.0
创建一个 API 项目。我成功发送了一个不带参数的POST请求。但目前我在尝试通过 Postman 发送带有 JSON 参数的 POST 请求时遇到问题,总是收到无效请求,如下所示。
请注意,查询字符串中还有 key
参数,用于使用我创建的中间件授权请求。这部分没有问题。
Controller 代码如下:
[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}!");
}
}
奇怪的是,我已经创建了 UserRequest 类并将其用作参数输入,如下所示:
public class UserRequest
{
public string Id { get; set; }
}
这是我的 Startup.cs
设置,我已经添加了 AddNewtonsoftJson
以启用 JSON 序列化器输入:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(option => option.EnableEndpointRouting = false)
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddNewtonsoftJson(opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
/*Other API, DB settings and services goes here*/
...
}
到目前为止,这是我的尝试:
- 在
UserRequest
类上添加了[BindProperties]
。仍然返回相同的错误。 - 删除了 Controller 参数上的
[FromBody]
。仍然返回相同的错误。 - 将
id
重命名为Id
以遵循UserRequest
类中的命名。仍然返回相同的错误。 在
Startup.cs
上添加了这段代码,这将执行return BadRequest(new ApiResponse(400, "Model state is not valid."));
:.ConfigureApiBehaviorOptions(options => { options.SuppressModelStateInvalidFilter = true; })
在
Startup.cs
上删除了这段代码.AddNewtonsoftJson(opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore)
它会返回这个:
{ "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." ] } }
如有任何帮助,我们将不胜感激。
12/11/2019 更新:以下是我处理 API key 请求的方式:
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);
}
最佳答案
正如评论中所讨论的,您的日志记录中间件导致了问题。 当您读取请求主体或响应主体时,您需要重置流以便其他中间件可以读取它(在本例中为 JsonSerializer)。
在您的日志记录中间件中,您将进行如下调用:
var body = await new StreamReader(request.Body).ReadToEndAsync();
在该方法返回之前,您需要重置该流:
request.Body.Seek(0, SeekOrigin.Begin);
这对于响应代码是相同的,例如
response.Body.Seek(0, SeekOrigin.Begin);
编辑
按照评论中的要求,这里是中间件代码的示例:
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Request.EnableBuffering();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
// Log the contents of body...
context.Request.Body.Seek(0, SeekOrigin.Begin);
await _next(context);
}
}
重置 Body
流位置的代码需要在调用 _next(context)
之前出现
关于c# - ASP.NET MVC Core 3.0 - 为什么来自主体的 API 请求不断返回!ModelState.IsValid?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58812020/