Как вернуть 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.