【问题标题】:How to return HTTP 500 from ASP.NET Core RC2 Web Api?如何从 ASP.NET Core RC2 Web Api 返回 HTTP 500?
【发布时间】:2016-10-14 02:07:20
【问题描述】:

回到 RC1,我会这样做:

[HttpPost]
public IActionResult Post([FromBody]string something)
{    
    try{
        // ...
    }
    catch(Exception e)
    {
         return new HttpStatusCodeResult((int)HttpStatusCode.InternalServerError);
    }
}

在 RC2 中,不再有 HttpStatusCodeResult,我找不到任何可以返回 500 类型的 IActionResult。

现在的方法与我要问的完全不同吗?我们是否不再在Controller 代码中尝试捕获?我们是否只是让框架向 API 调用者抛出一个通用的 500 异常?对于开发,我如何才能看到确切的异常堆栈?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc


    【解决方案1】:

    Microsoft.AspNetCore.Mvc 的内置 Problem() 方法将根据 RFC 7807(在 ASP.NET Core 3.0 及更高版本中)返回“问题详细信息”响应。 只要没有明确设置其他状态,它将始终返回状态代码 500。

    [HttpPost]
    public IActionResult Post([FromBody] string value)
    {
        try
        {
            // ...
        }
        catch (Exception ex)
        {
            return Problem(
                //all parameters are optional:
                //detail: "Error while processing posted data."; //an explanation, ex.Stacktrace, ...
                //instance: "/city/London"  //A reference that identifies the specific occurrence of the problem
                //title: "An error occured." //a short title, maybe ex.Message
                //statusCode: StatusCodes.Status504GatewayTimeout, //will always return code 500 if not explicitly set
                //type: "http://example.com/errors/error-123-details"  //a reference to more information
                );
        }           
    }
    

    不设置任何参数,它将返回:

    {
        "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
        "title": "An error occured while processing your request.",
        "status": 500,
        "traceId": "|fadaed95-4d06eb16160e4996."
    }
    

    有关“问题详细信息”参数的更多信息: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-5.0

    【讨论】:

      【解决方案2】:

      对于aspnetcore-3.1,您也可以使用Problem(),如下所示;

      https://docs.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-3.1

       [Route("/error-local-development")]
      public IActionResult ErrorLocalDevelopment(
          [FromServices] IWebHostEnvironment webHostEnvironment)
      {
          if (webHostEnvironment.EnvironmentName != "Development")
          {
              throw new InvalidOperationException(
                  "This shouldn't be invoked in non-development environments.");
          }
      
          var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
      
          return Problem(
              detail: context.Error.StackTrace,
              title: context.Error.Message);
      }
      

      【讨论】:

      • NET5 也是如此
      【解决方案3】:

      如果您不想硬编码特定数字,可以使用 Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodeMicrosoft.AspNetCore.Http.StatusCodes 来形成您的回复。

      return  StatusCode(StatusCodes.Status500InternalServerError);
      

      更新:2019 年 8 月

      也许与原始问题没有直接关系,但是当尝试使用Microsoft Azure Functions 获得相同的结果时,我发现我必须在Microsoft.AspNetCore.Mvc.Core 程序集中构造一个新的StatusCodeResult 对象。我的代码现在看起来像这样;

      return new StatusCodeResult(StatusCodes.Status500InternalServerError);
      

      【讨论】:

      • 很棒,避免了任何硬编码部分/“幻数”。我以前用过 StatusCode((int)HttpStatusCode.InternalServerError) 但我更喜欢你的。
      • 我当时没有考虑的一件事是它使代码更具可读性,回到它你知道错误号 500 与什么相关,它就在代码中。自我记录:-)
      • 我无法想象内部服务器错误 (500) 会很快改变。
      • 太棒了。这也确实清理了我的招摇属性。例如:[ProducesResponseType(StatusCodes.Status500InternalServerError)]
      【解决方案4】:

      当你想在 MVC .Net Core 中返回 JSON 响应时你也可以使用:

      Response.StatusCode = (int)HttpStatusCode.InternalServerError;//Equals to HTTPResponse 500
      return Json(new { responseText = "my error" });
      

      这将返回 JSON 结果和 HTTPStatus。我用它来将结果返回给 jQuery.ajax()。

      【讨论】:

      • 我不得不使用return new JsonResult ...,但效果很好。
      【解决方案5】:
      return StatusCode((int)HttpStatusCode.InternalServerError, e);
      

      应在非 ASP.NET 上下文中使用(请参阅 ASP.NET Core 的其他答案)。

      HttpStatusCodeSystem.Net 中的一个枚举。

      【讨论】:

        【解决方案6】:

        据我所知,ControllerBase 类中有一些辅助方法。只需使用StatusCode 方法:

        [HttpPost]
        public IActionResult Post([FromBody] string something)
        {    
            //...
            try
            {
                DoSomething();
            }
            catch(Exception e)
            {
                 LogException(e);
                 return StatusCode(500);
            }
        }
        

        您也可以使用StatusCode(int statusCode, object value) 重载来协商内容。

        【讨论】:

        • 这样做,我们会丢失 CORS 标头,因此浏览器客户端会隐藏错误。 V 令人沮丧。
        • @bbsimonbb 内部错误应该对客户隐藏。应该为开发人员记录它们。
        • 开发人员应该拥有(传统上享有)选择返回错误信息级别的特权。
        【解决方案7】:

        如果您在回复中需要正文,可以致电

        return StatusCode(StatusCodes.Status500InternalServerError, responseObject);
        

        这将返回一个带有响应对象的 500...

        【讨论】:

        • 如果您不想创建特定的响应对象类型:return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" }); 当然,您可以根据需要添加描述性消息以及其他元素。
        【解决方案8】:

        如何创建一个自定义 ObjectResult 类来表示内部服务器错误,例如 OkObjectResult 的错误? 您可以在自己的基类中放置一个简单的方法,这样您就可以像 Ok()BadRequest() 一样轻松生成 InternalServerError 并返回它。

        [Route("api/[controller]")]
        [ApiController]
        public class MyController : MyControllerBase
        {
            [HttpGet]
            [Route("{key}")]
            public IActionResult Get(int key)
            {
                try
                {
                    //do something that fails
                }
                catch (Exception e)
                {
                    LogException(e);
                    return InternalServerError();
                }
            }
        }
        
        public class MyControllerBase : ControllerBase
        {
            public InternalServerErrorObjectResult InternalServerError()
            {
                return new InternalServerErrorObjectResult();
            }
        
            public InternalServerErrorObjectResult InternalServerError(object value)
            {
                return new InternalServerErrorObjectResult(value);
            }
        }
        
        public class InternalServerErrorObjectResult : ObjectResult
        {
            public InternalServerErrorObjectResult(object value) : base(value)
            {
                StatusCode = StatusCodes.Status500InternalServerError;
            }
        
            public InternalServerErrorObjectResult() : this(null)
            {
                StatusCode = StatusCodes.Status500InternalServerError;
            }
        }
        

        【讨论】:

        • 谢谢,我们有一个类库,它有一个返回 ActionResult 的方法,因此返回 StatusCode(500) 的常规解决方案在这种情况下不起作用,我们需要自定义 ObjectResult。
        【解决方案9】:

        截至目前 (1.1) 处理此问题的更好方法是在 Startup.csConfigure() 中执行此操作:

        app.UseExceptionHandler("/Error");
        

        这将执行/Error 的路由。这将使您免于在编写的每个操作中添加 try-catch 块。

        当然,您需要添加一个类似于此的 ErrorController:

        [Route("[controller]")]
        public class ErrorController : Controller
        {
            [Route("")]
            [AllowAnonymous]
            public IActionResult Get()
            {
                return StatusCode(StatusCodes.Status500InternalServerError);
            }
        }
        

        更多信息here


        如果您想获取实际的异常数据,可以将其添加到上述Get() return 语句之前。

        // Get the details of the exception that occurred
        var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        
        if (exceptionFeature != null)
        {
            // Get which route the exception occurred at
            string routeWhereExceptionOccurred = exceptionFeature.Path;
        
            // Get the exception that occurred
            Exception exceptionThatOccurred = exceptionFeature.Error;
        
            // TODO: Do something with the exception
            // Log it with Serilog?
            // Send an e-mail, text, fax, or carrier pidgeon?  Maybe all of the above?
            // Whatever you do, be careful to catch any exceptions, otherwise you'll end up with a blank page and throwing a 500
        }
        

        上面的 sn-p 取自Scott Sauber's blog

        【讨论】:

        • 这太棒了,但我如何记录抛出的异常?
        • @redwards510 这是你的做法:scottsauber.com/2017/04/03/… 我会更新我的答案以反映它,因为这是一个非常常见的用例?
        • @gldraphael 我们目前使用的是 Core 2.1。 Scott 的博客很棒,但我很好奇使用 IExceptionHandlerPathFeature 是否是当前推荐的最佳实践。也许创建自定义中间件更好?
        • @Pavel 我们在这里使用ExceptionHandler 中间件。当然,您可以自己滚动或按您认为合适的方式扩展它。这是link to the sources。编辑:See this lineIExceptionHandlerPathFeature
        猜你喜欢
        • 2016-03-16
        • 1970-01-01
        • 2017-01-17
        • 2018-10-04
        • 2016-11-14
        • 2016-12-17
        • 2021-09-24
        • 2017-12-10
        • 2018-03-29
        相关资源
        最近更新 更多