加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-core – 从显式类型的ASP.NET Core API控制器(不是IActi

发布时间:2020-12-16 00:20:39 所属栏目:asp.Net 来源:网络整理
导读:ASP.NET Core API控制器通常返回显式类型(默认情况下,如果您创建一个新项目),例如: [Route("api/[controller]")]public class ThingsController : Controller{ // GET api/things [HttpGet] public async TaskIEnumerableThing GetAsync() { //... } // G
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 question和this 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?添加如此基本的东西似乎需要付出很多努力。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读