Подтвердить что ты не робот

Обработка ошибок (отправка ex.Message клиенту)

У меня есть приложение ASP.NET Core 1.0 Web API, и я пытаюсь выяснить, как передать сообщение об исключении клиенту, если функция, которую мой контроллер вызывает из-за ошибок.

Я пробовал так много вещей, но ничего не реализует IActionResult.

Я не понимаю, почему это не обычное дело, которое нужно людям. Если нет правдивого решения, может кто-нибудь сказать мне, почему?

Я вижу некоторую документацию там, используя HttpResponseException(HttpResponseMessage), но для того, чтобы использовать это, я должен установить Compat Shim. Есть ли новый способ сделать это в Core 1.0?

Вот что я пробовал с прокладкой, но она не работает:

// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
    Customer c = _customersService.GetCustomerById(id);
    if (c == null)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
            StatusCode = HttpStatusCode.NotFound

        };

        throw new HttpResponseException(response);

        //return NotFound();
    }
    return new ObjectResult(c);
}

Когда возникает HttpResponseException, я смотрю на клиента и не могу найти сообщение, которое отправляю, в содержимом.

4b9b3361

Ответ 1

Вот простая ошибка класса DTO

public class ErrorDto
{
    public int Code {get;set;}
    public string Message { get; set; }

    // other fields

    public override string ToString()
    {
        return JsonConvert.SerializeObject(this);
    }
}

И затем используя ExceptionHandler промежуточное ПО:

            app.UseExceptionHandler(errorApp =>
            {
                errorApp.Run(async context =>
                {
                    context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
                    context.Response.ContentType = "application/json";

                    var error = context.Features.Get<IExceptionHandlerFeature>();
                    if (error != null)
                    {
                        var ex = error.Error;

                        await context.Response.WriteAsync(new ErrorDto()
                        {
                            Code = <your custom code based on Exception Type>,
                            Message = ex.Message // or your custom message
                            // other custom data
                        }.ToString(), Encoding.UTF8);
                    }
                });
            });

Ответ 2

Да, можно изменить код состояния на все, что вам нужно:

В вашем файле CustomExceptionFilterAttribute.cs измените код следующим образом:

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        context.Result = new ContentResult
        {
            Content = $"Error: {exception.Message}",
            ContentType = "text/plain",
            // change to whatever status code you want to send out
            StatusCode = (int?)HttpStatusCode.BadRequest 
        };
    }
}

Это в значительной степени.

Если у вас есть пользовательские исключения, вы можете также проверить их при захвате исключенного из контекста исключения. После этого вы можете отправлять разные коды состояния HTTP, основываясь на том, что произошло в вашем коде.

Надеюсь, что это поможет.

Ответ 3

Вы можете создать настраиваемый фильтр исключений, как показано ниже

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        var exception = context.Exception;
        context.Result = new JsonResult(exception.Message);
    }
}

Затем примените вышеуказанный атрибут к вашему контроллеру.

[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
     // GET: api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        throw new Exception("Suckers");
        return new string[] { "value1", "value2" };
    }
}

Ответ 4

Вместо того, чтобы поднимать и ловить исключение, как насчет упрощения вашего действия:

// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
    var customer = _customersService.GetCustomerById(id);

    if (customer == null)
    {
        return NotFound("Customer doesn't exist");        
    }

    return Ok(customer);
}

Я написал сообщение в блоге с дополнительными параметрами, такими как возврат объекта JSON вместо текста.

Ответ 5

Может быть, это полезно. Вы можете вернуть только object и отправили, например, BadRequest (HTTP CODE: 400) с вашим пользовательским object как фактическим параметром (я просто использовал интерполированную строку здесь), но вы можете вставить что угодно.

В вашей клиентской стороне вы можете поймать эту ситуацию с ошибкой, например, с помощью обработчика ошибок AJAX.

// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{

    var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();

    var geodataItems = _context.TruckFahrerGeoData;

    foreach (var truckFahrerGeoData in geodataItems)
    {
        GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);

        if (geoTelemetryData == null)
        {
            return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
        }
        TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
        {
            Speed = geoTelemetryData.Speed,
            Accuracy = geoTelemetryData.Accuracy,
            TruckAppId = geoTelemetryData.Activity.TruckAppId,
            TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
            ClId = geoTelemetryData.Activity.ClId,
            TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
            TaskId = geoTelemetryData.Activity.TaskId,
            TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
        };

        truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
    }


    return truckFahrerGeoDataItems;
}

Или еще более чистый способ с IActionResult следующим образом:

// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{

    var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();

    var geodataItems = _context.TruckFahrerGeoData;

    foreach (var truckFahrerGeoData in geodataItems)
    {
        GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);

        if (geoTelemetryData == null)
        {
            return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
        }
        TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
        {
            Speed = geoTelemetryData.Speed,
            Accuracy = geoTelemetryData.Accuracy,
            TruckAppId = geoTelemetryData.Activity.TruckAppId,
            TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
            ClId = geoTelemetryData.Activity.ClId,
            TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
            TaskId = geoTelemetryData.Activity.TaskId,
            TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
        };

        truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
    }


    return Ok(truckFahrerGeoDataItems);
}

Ответ 6

У меня была такая же проблема, и после некоторых исследований я обнаружил, что могу использовать HttpClient для вызова своего API и легко читать ответ. HttpClient не выдает никакой ошибки, если HTTP-ответ содержит код ошибки, но устанавливает для свойства IsSuccessStatusCode значение false.

Это моя функция с использованием HttpClient. Я звоню из моего контроллера.

  public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
        {
            string uri = apiUrl + url;
            using (var client = new HttpClient())
            {
                //client.BaseAddress = new Uri(uri);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
                HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));

                return response;
            }
        }

Это код моего контроллера, где я вызываю функцию, читаю ответ и определяю, есть ли у меня ошибка или нет, и отвечаю соответствующим образом. Обратите внимание, что я проверяю IsSuccessStatusCode.

                HttpResponseMessage response;
                string url = $"Setup/AddDonor";
                var postdata = JsonConvert.SerializeObject(donor);

                response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
                //var headers = response.Headers.Concat(response.Content.Headers);
                var responseBody = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode)
                {
                    tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));

                    return Json(new
                    {
                        ok = true,
                        message = tnxresult.Message,
                        statusCode = tnxresult.StatusCode
                    });
                }
                else
                {
                  ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));

                    return Json(new
                    {
                        ok = false,
                        message = rs.Message,
                        statusCode = rs.StatusCode
                    });

                }

Мой API возвращает сообщения об ошибках в формате JSON. Если вызов успешен, я упаковываю ответ также в JSON.

Важнейшая строка кода вот эта...

var responseBody = await response.Content.ReadAsStringAsync();

Он сериализует содержимое HTTP в строку как асинхронную операцию.

После этого я могу преобразовать свою строку JSON в объект и получить доступ к сообщению об ошибке/успеху и коду состояния.