¿Cómo devolver HTTP 500 desde ASP.NET Core RC2 Web Api?

189

De vuelta en RC1, haría esto:

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

En RC2, ya no hay HttpStatusCodeResult, y no hay nada que pueda encontrar que me permita devolver un tipo 500 de IActionResult.

¿El enfoque ahora es completamente diferente para lo que estoy preguntando? ¿Ya no intentamos atrapar el Controllercódigo? ¿Dejamos que el marco arroje una excepción genérica 500 al llamador API? Para el desarrollo, ¿cómo puedo ver la pila de excepción exacta?

Mickael Caruso
fuente

Respuestas:

242

Por lo que puedo ver, hay métodos auxiliares dentro de la ControllerBaseclase. Solo usa el StatusCodemétodo:

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

También puede usar la StatusCode(int statusCode, object value)sobrecarga que también negocia el contenido.

Federico Dipuma
fuente
77
Al hacer esto, perdemos los encabezados CORS, por lo que los errores se ocultan a los clientes del navegador. V frustrante.
bbsimonbb
2
@bbsimonbb Los errores internos deben ocultarse a los clientes. Deben estar registrados para los desarrolladores.
Himalaya Garg
10
Los desarrolladores deberían tener, tradicionalmente disfrutado, la prerrogativa de elegir qué nivel de información de error se devuelve.
bbsimonbb
179

Puede usar Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodey Microsoft.AspNetCore.Http.StatusCodespara formar su respuesta, si no desea codificar números específicos.

return  StatusCode(StatusCodes.Status500InternalServerError);

ACTUALIZACIÓN: agosto de 2019

Quizás no esté directamente relacionado con la pregunta original, pero al tratar de lograr el mismo resultado Microsoft Azure Functions, descubrí que tenía que construir un nuevo StatusCodeResultobjeto encontrado en el Microsoft.AspNetCore.Mvc.Coreensamblaje. Mi código ahora se ve así;

return new StatusCodeResult(StatusCodes.Status500InternalServerError);
Edward Comeau
fuente
11
Excelente, evita las partes codificadas / "números mágicos". He usado StatusCode ((int) HttpStatusCode.InternalServerError) antes pero me gusta más el tuyo.
aleor
1
Una cosa que no consideré en ese momento es que hace que el código sea más legible, volviendo a él, sabes a qué se relaciona el error número 500, está allí en el código. Autodocumentado :-)
Edward Comeau
11
No puedo imaginar el error interno del servidor (500) cambiando pronto.
llega el
2
increíble. Esto también realmente limpia mis atributos de arrogancia. ex: [ProducesResponseType (StatusCodes.Status500InternalServerError)]
redwards510
43

Si necesita un cuerpo en su respuesta, puede llamar

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

Esto devolverá un 500 con el objeto de respuesta ...

David McEleney
fuente
3
Si no desea crear un tipo de objeto de respuesta específico: return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" });Y, por supuesto, puede agregar un mensaje tan descriptivo como desee, y también otros elementos.
Mike Taverne
18

Una mejor manera de manejar esto a partir de ahora (1.1) es hacer esto en Startup.cs's Configure():

app.UseExceptionHandler("/Error");

Esto ejecutará la ruta para /Error. Esto le ahorrará agregar bloques try-catch a cada acción que escriba.

Por supuesto, deberá agregar un ErrorController similar a este:

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

Más información aquí .


En caso de que desee obtener los datos de excepción reales, puede agregar esto a lo anterior Get()justo antes de la returndeclaración.

// 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
}

Fragmento anterior tomado del blog de Scott Sauber .

gldraphael
fuente
Esto es increíble, pero ¿cómo puedo registrar la excepción que se produjo?
redwards510
@ redwards510 Así es como lo haces: scottsauber.com/2017/04/03/… Actualizaré mi respuesta para reflejarla, ya que es un caso de uso muy común 😊
gldraphael
@gldraphael Actualmente estamos utilizando Core 2.1. El blog de Scott es genial, pero tengo curiosidad por saber si usar IExceptionHandlerPathFeature es actualmente la mejor práctica recomendada. ¿Quizás es mejor crear middleware personalizado?
Pavel
@Pavel estamos usando el ExceptionHandlermiddleware aquí. Por supuesto, puede rodar el suyo o extenderlo como mejor le parezca. Aquí está el enlace a las fuentes . EDITAR: Vea esta línea para IExceptionHandlerPathFeature .
gldraphael
15
return StatusCode((int)HttpStatusCode.InternalServerError, e);

Debe usarse en contextos que no sean ASP.NET (consulte otras respuestas para ASP.NET Core).

HttpStatusCodees una enumeración en System.Net.

Shimmy Weitzhandler
fuente
12

¿Qué tal crear una clase ObjectResult personalizada que represente un error interno del servidor como el indicado OkObjectResult? Puede poner un método simple en su propia clase base para que pueda generar fácilmente el InternalServerError y devolverlo tal como lo hace Ok()o 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;
    }
}
Airn5475
fuente
6

Cuando desee devolver una respuesta JSON en MVC .Net Core También puede usar:

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

Esto devolverá el resultado JSON y HTTPStatus. Lo uso para devolver resultados a jQuery.ajax ().

Tekin
fuente
1
Tuve que usar, return new JsonResult ...pero por lo demás funcionó muy bien.
Mike Taverne
5

Para aspnetcore-3.1, también puede usar lo Problem()siguiente;

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);
}
Teoman shipahi
fuente