如何从ASP.NET Core RC2 Web Api返回HTTP 500?

336

回到 RC1 时,我会这样做:

[HttpPost]
public IActionResult Post([FromBody]string something)
{    
    try{
        // ...
    }
    catch(Exception e)
    {
         return new HttpStatusCodeResult((int)HttpStatusCode.InternalServerError);
    }
}
在 RC2 版本中,不再有 HttpStatusCodeResult,我找不到任何可以让我返回 500 类型的 IActionResult。
现在的做法是完全不同的吗?我们不再在 Controller 代码中尝试捕捉异常吗?我们只是让框架将一个通用的 500 异常抛回给 API 调用者吗?对于开发,我该如何查看确切的异常堆栈?
13个回答

393

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

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

您还可以使用 StatusCode(int statusCode, object value) 重载函数,它也会协商内容。


10
这样做会丢失CORS头,导致浏览器客户端无法看到错误信息。非常令人沮丧。 - bbsimonbb
5
内部错误应该对客户端隐藏。开发人员应该记录这些错误。 - Himalaya Garg
33
开发人员通常享有选择返回何种级别的错误信息的特权。 - bbsimonbb
5
更好的写法:return StatusCode(StatusCodes.Status500InternalServerError); - Andrew

268

如果您不希望硬编码特定的数字,可以使用 Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodeMicrosoft.AspNetCore.Http.StatusCodes 来构建响应。

return  StatusCode(StatusCodes.Status500InternalServerError);

更新:2019年8月

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

return new StatusCodeResult(StatusCodes.Status500InternalServerError);

15
伟大的代码,避免任何硬编码部分或“魔法数字”。我以前用过StatusCode((int)HttpStatusCode.InternalServerError),但我更喜欢你的。 - aleor
2
当时我没有考虑到的一点是,它使得代码更易读,在回头查看时,你知道错误号500所指的内容,在代码里就直接写明了。自我记录 :-) - Edward Comeau
28
我无法想象内部服务器错误(500)在短时间内会有所改变。 - rollsch
4
太棒了。这也真的整理了我的 Swagger 属性。例如:[ProducesResponseType(StatusCodes.Status500InternalServerError)] - redwards510

109

如果您需要在响应中包含正文内容,可以调用

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

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


11
如果您不想创建特定的响应对象类型:return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" }); 当然,您可以添加尽可能详细的消息以及其他元素。 - Mike Taverne

42
针对aspnetcore-3.1,您也可以像下面这样使用Problem()https://learn.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);
}

1
相同的情况也适用于 NET5。 - RickWeb

27

截至目前(1.1版本),更好的处理方法是在Startup.cs文件中的Configure()方法中执行此操作:

A better way to handle this as of now (1.1) is to do this in Startup.cs's Configure():

app.UseExceptionHandler("/Error");

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

当然,您需要添加类似于以下内容的ErrorController:

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

更多信息请点击此处


如果您想获取实际的异常数据,在上述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
}

以上代码片段摘自Scott Sauber的博客


这太棒了,但我该如何记录抛出的异常? - redwards510
@redwards510 这是如何实现的:https://scottsauber.com/2017/04/03/adding-global-error-handling-and-logging-in-asp-net-core/ 我会更新我的答案以反映它,因为这是一个非常常见的用例。 - galdin
@gldraphael 我们目前正在使用Core 2.1。Scott的博客很棒,但我想知道是否使用IExceptionHandlerPathFeature是当前推荐的最佳实践。也许创建自定义中间件更好? - Pavel
@Pavel,我们在这里使用ExceptionHandler中间件。当然,您可以根据需要自己编写或扩展它。这是源代码链接。编辑:请参见此行IExceptionHandlerPathFeature - galdin

26

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

[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。 - iBobb
真惊奇选项中没有包含这个,不过没关系。足够简单。谢谢! - interesting-name-here

22
return StatusCode((int)HttpStatusCode.InternalServerError, e);

对于非ASP.NET上下文应该使用(有关ASP.NET Core请参见其他答案)。

HttpStatusCodeSystem.Net中的枚举类型。


10

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

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

这将返回JSON结果和HTTP状态码。我用它来将结果返回给jQuery.ajax()。


1
我不得不使用 return new JsonResult ...,但除此之外一切都很好。 - Mike Taverne

8
微软.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://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-5.0


6
您可以使用以下代码:
return StatusCode(500,"message");

这是一个示例代码:
public Task<IActionResult> GetById(int courseId)
{
  try
  {
     var result = await _mediator.Send(new GetCourse(courseId));
     return Ok(result);
  }
  catch(Exception ex) 
  {
     return StatusCode(500,ex.Message);
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接