Come restituire HTTP 500 da ASP.NET Core RC2 Web Api?


189

Torna in RC1, vorrei fare questo:

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

In RC2, non c'è più HttpStatusCodeResult e non riesco a trovare nulla che mi permetta di restituire un 500 tipo di IActionResult.

L'approccio ora è completamente diverso per quello che sto chiedendo? Non proviamo più a catturare il Controllercodice? Lasciamo che il framework restituisca un'eccezione 500 generica al chiamante API? Per lo sviluppo, come posso vedere lo stack esatto delle eccezioni?

Risposte:


242

Da quello che posso vedere ci sono metodi di supporto all'interno della ControllerBaseclasse. Basta usare il StatusCodemetodo:

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

È inoltre possibile utilizzare il StatusCode(int statusCode, object value)sovraccarico che negozia anche il contenuto.


7
facendo ciò, perdiamo le intestazioni CORS, quindi gli errori vengono nascosti dai client del browser. V frustrante.
bbsimonbb,

2
@bbsimonbb Gli errori interni devono essere nascosti ai client. Dovrebbero essere registrati per gli sviluppatori.
Himalaya Garg,

10
Gli sviluppatori dovrebbero avere, per tradizione, godere della prerogativa di scegliere quale livello di informazioni sull'errore deve essere restituito.
bbsimonbb,

179

È possibile utilizzare Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodee Microsoft.AspNetCore.Http.StatusCodesformare la risposta, se non si desidera codificare in modo specifico numeri specifici.

return  StatusCode(StatusCodes.Status500InternalServerError);

AGGIORNAMENTO: ago 2019

Forse non direttamente correlato alla domanda originale ma quando Microsoft Azure Functionsho cercato di ottenere lo stesso risultato ho scoperto che dovevo costruire un nuovo StatusCodeResultoggetto trovato Microsoft.AspNetCore.Mvc.Corenell'assieme. Il mio codice ora è simile al seguente;

return new StatusCodeResult(StatusCodes.Status500InternalServerError);

11
Grande, evita qualsiasi parte codificata / "numeri magici". Ho già usato StatusCode ((int) HttpStatusCode.InternalServerError) ma mi piace di più il tuo.
aleor

1
Una cosa che non ho considerato al momento è che rende il codice più leggibile, tornando ad esso sai a cosa si riferisce l'errore numero 500, è proprio lì nel codice. Autodocumentazione :-)
Edward Comeau,

11
Non riesco a immaginare l'errore interno del server (500) che cambia presto.
lancia il

2
eccezionale. anche questo pulisce davvero i miei attributi spavalderia. es: [ProducesResponseType (StatusCodes.Status500InternalServerError)]
redwards510

43

Se hai bisogno di un corpo nella tua risposta, puoi chiamare

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

Questo restituirà un 500 con l'oggetto response ...


3
Se non si desidera creare un tipo di oggetto di risposta specifico: return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" });E, naturalmente, è possibile aggiungere un messaggio descrittivo a piacere e anche altri elementi.
Mike Taverne,

18

Un modo migliore per gestire questo fin d'ora (1.1) è quello di fare questo in Startup.cs's Configure():

app.UseExceptionHandler("/Error");

Questo eseguirà il percorso per /Error. Questo ti eviterà di aggiungere blocchi try-catch a ogni azione che scrivi.

Ovviamente, dovrai aggiungere un ErrorController simile a questo:

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

Maggiori informazioni qui .


Nel caso in cui si desideri ottenere i dati effettivi delle eccezioni, è possibile aggiungerli a quanto sopra Get()prima returndell'istruzione.

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

Sopra lo snippet tratto dal blog di Scott Sauber .


è fantastico, ma come posso registrare l'eccezione che è stata lanciata?
redwards510

@ redwards510 Ecco come lo fai: scottsauber.com/2017/04/03/… Aggiornerò la mia risposta per rifletterla, dal momento che è un caso d'uso molto comune 😊
gldraphael,

@gldraphael Attualmente stiamo utilizzando Core 2.1. Il blog di Scott è fantastico, ma sono curioso di sapere se l'utilizzo di IExceptionHandlerPathFeature sia attualmente le migliori pratiche consigliate. Forse creare middleware personalizzato è meglio?
Pavel,

@Pavel stiamo usando il ExceptionHandlermiddleware qui. Naturalmente, puoi tirare il tuo o estenderlo come ritieni opportuno. Ecco il link alle fonti . EDIT: vedere questa riga per IExceptionHandlerPathFeature .
Gldraphael,

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

Dovrebbe essere usato in contesti non ASP.NET (vedi altre risposte per ASP.NET Core).

HttpStatusCodeè un'enumerazione in System.Net.


12

Che ne dici di creare una classe ObjectResult personalizzata che rappresenti un errore interno del server come quello per OkObjectResult? Puoi inserire un metodo semplice nella tua classe di base in modo da poter generare facilmente InternalServerError e restituirlo come fai tu 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;
    }
}

6

Quando si desidera restituire una risposta JSON in MVC .Net Core È inoltre possibile utilizzare:

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

Ciò restituirà sia il risultato JSON che HTTPStatus. Lo uso per restituire risultati a jQuery.ajax ().


1
Ho dovuto usare return new JsonResult ...ma per il resto ha funzionato alla grande.
Mike Taverne,

5

Per aspnetcore-3.1, puoi anche usare Problem()come di seguito;

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.