如何从 Invoke 方法 return BadRequestObjectResult

How to return BadRequestObjectResult from Invoke method

我正在开发一个 API,它最初验证来自 https 请求的接受 header,然后通过中间件进行响应。我已经在调用方法中添加了验证 Accept header 的逻辑,如图所示,How do I return bad request object if the result of the validation(string comparison) is false.

//Invoke method 
public async Task<ObjectResult> Invoke(HttpContext context)
{
   bool result = context.Request.Headers["Accept"].ToString() == 
   "app/version.abc-ghi-api.v";
   if (result == true)
   {
     await  _next(context);   
   }
   ObjectResult objectResult = await 
   Error.GenerateErrorMessage("Accept header validation 
   failed", Log.Logger);
   return objectResult;  
}

//Error class
public class Error
{
    public async static Task<ObjectResult> Error(string message, logger log)
    {
       //logic for creating the payload
       return new BadRequestObjectResult(errorMessagePayload)
    }
}

//Startup class configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
     app.UseMiddleware<AcceptHeaderMiddleware>();
     if (env.IsDevelopment())
     {   
         app.UseDeveloperExceptionPage();
     }
     else
     {          
         app.UseHsts();
     }

     app.UseMvc();
}

我期待 badObjectResult(当验证失败时)作为响应,但我得到 200 OK 响应和空白响应 body。

IActionResult 类型仅在 MVC 管道中使用。作为该管道的一部分,然后执行操作或 MVC 过滤器的结果 objects 以在 HttpResponse object.

上创建实际响应

然而,这意味着您实际上不能在 MVC 管道之外使用操作结果 objects。因此,如果您有自定义中间件,您将无法使用这些结果,因为它们不会被 MVC 管道处理(从技术上讲,您 可以 自己执行结果,但我不建议这样做)。

因此您必须自己设置结果。如果你只是想设置一些(失败)状态码,那很容易做到:

public async Task<ObjectResult> Invoke(HttpContext context)
{
    bool result = context.Request.Headers["Accept"].ToString() == "app/version.abc-ghi-api.v";
    if (result)
    {
        await  _next(context);   
    }
    else
    {
        context.Result.StatusCode = 500;
    }
}

如果你想包含一个 body,那么它会变得有点复杂,因为你现在必须写入输出流,这也意味着你首先必须正确地序列化你的输出。

因此,与其在自定义中间件中执行此操作,我建议您在 MVC 过滤器中执行此操作。如上所述,filters运行作为MVC管道的一部分,所以它们只运行与MVC中间件有关。这意味着您将无法以这种方式保护您的静态文件——但通常这没什么大不了的。

在您的情况下,由于您想通过 HTTP header 授权客户端,我建议您创建一个 authorization filter:

public class AcceptHeaderAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        bool result = context.HttpContext.Request.Headers["Accept"].ToString() == "app/version.abc-ghi-api.v";
        if (!result)
        {
            var result = new Error.GenerateErrorMessage("Accept header validation failed", Log.Logger);
            context.Result = result;
        }
    }
}

这现在使用 MVC 管道,因此您可以使用IActionResultobjects。由于您在授权过滤器中设置结果,因此您也是 short-circuiting 剩余的管道,因此之后不会执行任何操作。执行将停止并立即产生您的结果。


最后一点:Accept header 有一个非常具体的用例,用于内容协商。这意味着当您将其设置为某种自定义内容类型时,服务器应 return 具有此类内容类型的结果。将它用于授权目的并不适合这里。