Как вернуть HTTP 500 из ASP.NET Core RC2 Web Api?


189

Вернувшись в 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? Для разработки, как я могу увидеть точный стек исключений?

Ответы:


242

Из того, что я вижу, внутри ControllerBaseкласса есть вспомогательные методы . Просто используйте StatusCodeметод:

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

Вы также можете использовать StatusCode(int statusCode, object value)перегрузку, которая также согласовывает содержимое.


7
при этом мы теряем заголовки CORS, поэтому ошибки скрыты от клиентов браузера. V расстраивает.
bbsimonbb

2
@bbsimonbb Внутренние ошибки должны быть скрыты от клиентов. Они должны быть зарегистрированы для разработчиков.
Гималаи Гарг

10
Разработчики должны иметь, как правило, прерогативу выбирать, какой уровень ошибки возвращается.
bbsimonbb

179

Вы можете использовать Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodeи Microsoft.AspNetCore.Http.StatusCodesдля формирования своего ответа, если вы не хотите жестко задавать конкретные номера.

return  StatusCode(StatusCodes.Status500InternalServerError);

ОБНОВЛЕНИЕ: август 2019

Возможно, это не имеет прямого отношения к исходному вопросу, но, пытаясь достичь того же результата, Microsoft Azure Functionsя обнаружил, что мне нужно создать новый StatusCodeResultобъект, найденный в Microsoft.AspNetCore.Mvc.Coreсборке. Мой код теперь выглядит так;

return new StatusCodeResult(StatusCodes.Status500InternalServerError);

11
Великий, избегает любых жестко закодированных частей / "магических чисел". Я использовал StatusCode ((int) HttpStatusCode.InternalServerError) раньше, но мне больше нравится ваш.
Aleor

1
Одна вещь, которую я не учел в то время, это то, что она делает код более читабельным, возвращаясь к нему, вы знаете, к чему относится ошибка с номером 500, прямо в коде. Самодокументирование :-)
Эдвард Комо

11
Я не могу представить, чтобы внутренняя ошибка сервера (500) изменилась в ближайшее время.
катится

2
здорово. это также действительно очищает мои атрибуты чванства. например: [ProducesResponseType (StatusCodes.Status500InternalServerError)]
redwards510

43

Если вам нужно тело в вашем ответе, вы можете позвонить

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

Это вернет 500 с объектом ответа ...


3
Если вы не хотите создавать определенный тип объекта ответа: return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" });и, конечно, вы можете добавить как описательное сообщение, как вам нравится, так и другие элементы.
Майк Таверн

18

Лучший способ справиться с этим , как сейчас (1.1) , чтобы сделать это в 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
}

Выше фрагмент взят из блога Скотта Саубера .


это круто, но как мне записать исключение, которое было сгенерировано?
redwards510

@ redwards510 Вот как вы это делаете: scottsauber.com/2017/04/03/… Я обновлю свой ответ, чтобы отразить его, так как это очень распространенный вариант использования 😊
gldraphael

@gldraphael В настоящее время мы используем Core 2.1. Блог Скотта великолепен, но мне любопытно, если использование IExceptionHandlerPathFeature в настоящее время является рекомендуемой передовой практикой. Возможно, лучше создать собственное промежуточное ПО?
Павел

@Pavel, мы используем ExceptionHandlerпромежуточное ПО здесь. Вы можете, конечно, свернуть свой собственный или расширить его по своему усмотрению. Вот ссылка на источники . РЕДАКТИРОВАТЬ: Смотрите эту строку для IExceptionHandlerPathFeature .
Gldraphael

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

Должен использоваться в не-ASP.NET контекстах (см. Другие ответы для ASP.NET Core).

HttpStatusCodeперечисление в System.Net.


12

Как насчет создания пользовательского класса 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;
    }
}

6

Если вы хотите вернуть ответ JSON в MVC .Net Core, вы также можете использовать:

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

Это вернет как JSON-результат, так и HTTPStatus. Я использую его для возврата результатов в jQuery.ajax ().


1
Я должен был использовать, return new JsonResult ...но в остальном работал отлично.
Майк Таверн

5

Для 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);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.