Comment renvoyer HTTP 500 à partir de l'API Web ASP.NET Core RC2?


189

De retour dans RC1, je ferais ceci:

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

Dans RC2, il n'y a plus HttpStatusCodeResult, et il n'y a rien que je puisse trouver qui me permette de renvoyer un type 500 de IActionResult.

L'approche est-elle maintenant entièrement différente de ce que je demande? N'essayons-nous plus d'attraper dans le Controllercode? Laissons-nous simplement le framework renvoyer une exception générique 500 à l'appelant d'API? Pour le développement, comment puis-je voir la pile d'exceptions exacte?

Réponses:


242

D'après ce que je peux voir, il existe des méthodes d'assistance à l'intérieur de la ControllerBaseclasse. Utilisez simplement la StatusCodeméthode:

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

Vous pouvez également utiliser la StatusCode(int statusCode, object value)surcharge qui négocie également le contenu.


7
ce faisant, nous perdons les en-têtes CORS, donc les erreurs sont masquées aux clients du navigateur. V frustrant.
bbsimonbb

2
@bbsimonbb Les erreurs internes doivent être cachées aux clients. Ils doivent être enregistrés pour les développeurs.
Himalaya Garg

10
Les développeurs devraient avoir, traditionnellement, la prérogative de choisir le niveau d'information d'erreur renvoyé.
bbsimonbb

179

Vous pouvez utiliser Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodeet Microsoft.AspNetCore.Http.StatusCodespour former votre réponse, si vous ne souhaitez pas coder en dur des numéros spécifiques.

return  StatusCode(StatusCodes.Status500InternalServerError);

MISE À JOUR: août 2019

Peut-être pas directement lié à la question d'origine, mais en essayant d'obtenir le même résultat avec, Microsoft Azure Functionsj'ai constaté que je devais construire un nouvel StatusCodeResultobjet trouvé dans l' Microsoft.AspNetCore.Mvc.Coreassemblage. Mon code ressemble maintenant à ceci;

return new StatusCodeResult(StatusCodes.Status500InternalServerError);

11
Excellent, évite toutes les parties codées en dur / "nombres magiques". J'ai déjà utilisé StatusCode ((int) HttpStatusCode.InternalServerError) mais j'aime mieux le vôtre.
aleor

1
Une chose que je n'ai pas considérée à l'époque est que cela rend le code plus lisible, en y revenant, vous savez à quoi se rapporte l'erreur numéro 500, c'est juste là dans le code. Self documenting :-)
Edward Comeau

11
Je ne peux pas imaginer que l'erreur de serveur interne (500) change de sitôt.
lance

2
impressionnant. cela nettoie aussi vraiment mes attributs swagger. ex: [ProducesResponseType (StatusCodes.Status500InternalServerError)]
redwards510

43

Si vous avez besoin d'un corps dans votre réponse, vous pouvez appeler

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

Cela renverra un 500 avec l'objet de réponse ...


3
Si vous ne souhaitez pas créer un type d'objet de réponse spécifique: return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" });Et bien sûr, vous pouvez ajouter un message aussi descriptif que vous le souhaitez, ainsi que d'autres éléments.
Mike Taverne

18

Une meilleure façon de gérer cela à partir de maintenant (1.1) est de le faire dans Startup.cs's Configure():

app.UseExceptionHandler("/Error");

Cela exécutera la route pour /Error . Cela vous évitera d'ajouter des blocs try-catch à chaque action que vous écrivez.

Bien sûr, vous devrez ajouter un ErrorController similaire à celui-ci:

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

Plus d'informations ici .


Si vous souhaitez obtenir les données d'exception réelles, vous pouvez l'ajouter à ce qui précède Get()juste avant l' returninstruction.

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

Ci-dessus, extrait du blog de Scott Sauber .


c'est génial, mais comment puis-je enregistrer l'exception qui a été levée?
redwards510

@ redwards510 Voici comment procéder: scottsauber.com/2017/04/03/ ... Je vais mettre à jour ma réponse pour la refléter, car c'est un cas d'utilisation très courant 😊
gldraphael

@gldraphael Nous utilisons actuellement Core 2.1. Le blog de Scott est génial, mais je suis curieux de savoir si l'utilisation de IExceptionHandlerPathFeature est actuellement les meilleures pratiques recommandées. Peut-être que créer un middleware personnalisé est-il préférable?
Pavel

@Pavel, nous utilisons le ExceptionHandlermiddleware ici. Vous pouvez, bien entendu, rouler le vôtre ou l'étendre comme bon vous semble. Voici le lien vers les sources . EDIT: Voir cette ligne pour IExceptionHandlerPathFeature .
gldraphael

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

Doit être utilisé dans des contextes non-ASP.NET (voir les autres réponses pour ASP.NET Core).

HttpStatusCodeest une énumération dans System.Net.


12

Que diriez-vous de créer une classe ObjectResult personnalisée qui représente une erreur de serveur interne comme celle pour OkObjectResult? Vous pouvez mettre une méthode simple dans votre propre classe de base afin de pouvoir facilement générer InternalServerError et la renvoyer comme vous le faites Ok()ou 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;
    }
}

6

Lorsque vous souhaitez renvoyer une réponse JSON dans MVC .Net Core, vous pouvez également utiliser:

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

Cela renverra à la fois le résultat JSON et HTTPStatus. Je l'utilise pour renvoyer les résultats à jQuery.ajax ().


1
J'ai dû utiliser return new JsonResult ...mais autrement fonctionnait très bien.
Mike Taverne

5

Pour aspnetcore-3.1, vous pouvez également utiliser Problem()comme ci-dessous;

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);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.