asp.net-core – 从显式类型的ASP.NET Core API控制器(不是IActionResult)返回404

前端之家收集整理的这篇文章主要介绍了asp.net-core – 从显式类型的ASP.NET Core API控制器(不是IActionResult)返回404前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
ASP.NET Core API控制器通常返回显式类型(默认情况下,如果您创建一个新项目),例如:
[Route("api/[controller]")]
public class ThingsController : Controller
{
    // GET api/things
    [HttpGet]
    public async Task<IEnumerable<Thing>> GetAsync()
    {
        //...
    }

    // GET api/things/5
    [HttpGet("{id}")]
    public async Task<Thing> GetAsync(int id)
    {
        Thing thingFromDB = await GetThingFromDBAsync();
        if(thingFromDB == null)
            return null; // This returns HTTP 204

        // Process thingFromDB,blah blah blah
        return thing;
    }

    // POST api/things
    [HttpPost]
    public void Post([FromBody]Thing thing)
    {
        //..
    }

    //... and so on...
}

问题是返回null; – 它返回HTTP 204:成功,没有内容

然后,许多客户端Javascript组件认为这是成功的,因此有如下代码

const response = await fetch('.../api/things/5',{method: 'GET' ...});
if(response.ok)
    return await response.json(); // Error,no content!

在线搜索(例如this questionthis answer)指向有用的返回NotFound();控制器的扩展方法,但所有这些都返回IActionResult,它与我的任务< Thing>不兼容。返回类型。该设计模式如下所示:

// GET api/things/5
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id)
{
    var thingFromDB = await GetThingFromDBAsync();
    if (thingFromDB == null)
        return NotFound();

    // Process thingFromDB,blah blah blah
    return Ok(thing);
}

这是有效的,但要使用它,GetAsync的返回类型必须更改为Task< IActionResult> – 显式输入丢失,并且控制器上的所有返回类型都必须改变(即根本不使用显式输入),或者存在混合,其中一些动作处理显式类型而其他动作处理。此外,单元测试现在需要对序列化做出假设,并在它们具有具体类型之前明确地反序列化IActionResult的内容

有很多方法可以解决这个问题,但它似乎是一个容易设计出来的令人困惑的混搭,所以真正的问题是:ASP.NET核心设计师的正确方法是什么?

似乎可能的选择是:

>根据预期的类型,有一个奇怪的(混乱的测试)显式类型和IActionResult混合。
>忘记显式类型,Core MVC并不真正支持它们,总是使用IActionResult(在这种情况下它们为什么会出现?)
>编写HttpResponseException的实现并将其用作ArgumentOutOfRangeException(有关实现,请参阅this answer)。但是,这确实需要使用程序流的例外,这通常是一个坏主意,也是deprecated by the MVC Core team
>编写HttpNoContentOutputFormatter的实现,为GET请求返回404
>我还缺少核心MVC应该如何工作的其他东西?
>或者有一个理由为什么204是正确的,404错误的GET请求失败?

这些都涉及妥协和重构,这些都会丢失一些东西,或者增加与MVC Core设计相悖的不必要的复杂性。哪个妥协是正确的,为什么?

解决方法

这是 addressed in ASP.NET Core 2.1与ActionResult< T>:
public ActionResult<Thing> Get(int id) {
    Thing thing = GetThingFromDB();

    if (thing == null)
        return NotFound();

    return thing;
}

甚至:

public ActionResult<Thing> Get(int id) =>
    GetThingFromDB() ?? NotFound();

一旦我实现了它,我会更详细地更新这个答案。

原始答案

在ASP.NET Web API 5中有一个HttpResponseException(正如Hackerman所指出的那样),但它已从Core中删除,并且没有中间件来处理它。

我认为这种变化是由于.NET Core – ASP.NET试图完成所有开箱即用的工作,ASP.NET Core只会执行你明确告诉它的事情(这是它为什么它如此快速和便携的重要部分) )。

我找不到一个现有的库,所以我自己写了。首先,我们需要一个自定义异常来检查:

public class StatusCodeException : Exception
{
    public StatusCodeException(HttpStatusCode statusCode)
    {
        StatusCode = statusCode;
    }

    public HttpStatusCode StatusCode { get; set; }
}

然后我们需要一个RequestDelegate处理程序来检查新的异常并将其转换为HTTP响应状态代码

public class StatusCodeExceptionHandler
{
    private readonly RequestDelegate request;

    public StatusCodeExceptionHandler(RequestDelegate pipeline)
    {
        this.request = pipeline;
    }

    public Task Invoke(HttpContext context) => this.InvokeAsync(context); // Stops VS from nagging about async method without ...Async suffix.

    async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await this.request(context);
        }
        catch (StatusCodeException exception)
        {
            context.Response.StatusCode = (int)exception.StatusCode;
            context.Response.Headers.Clear();
        }
    }
}

然后我们在Startup.Infigure中注册这个中间件:

public class Startup
{
    ...

    public void Configure(IApplicationBuilder app)
    {
        ...
        app.UseMiddleware<StatusCodeExceptionHandler>();

最后,操作可以抛出HTTP状态代码异常,同时仍然返回一个显式类型,可以轻松地进行单元测试而无需从IActionResult转换:

public Thing Get(int id) {
    Thing thing = GetThingFromDB();

    if (thing == null)
        throw new StatusCodeException(HttpStatusCode.NotFound);

    return thing;
}

这保留了返回值的显式类型,并允许轻松区分成功的空结果(返回null;)和错误,因为无法找到某些内容(我认为它就像抛出ArgumentOutOfRangeException)。

虽然这是问题的解决方案,但它仍然没有真正回答我的问题 – Web API的设计者构建了对显式类型的支持,期望它们将被使用,添加了返回null的特定处理;这样它会产生204而不是200,然后没有添加任何方式来处理404添加如此基本的东西似乎需要付出很多努力。

猜你在找的.NET Core相关文章