При возвращении IEnumerable из контроллера не перехватывается Exception

131
19 мая 2021, 05:20

Есть метод в контроллере который возвращает IEnumerable, в процессе конвейера LINQ происходит Exception и фильтр эксепшенов не перехватывает этот эксепшен и клиенту приходит поломанный массив:

[Route("api/[controller]")]
public class ValuesController : Controller {
    [HttpGet]
    public IEnumerable<int> Get() {
        var a = new[] {
            1,
            2,
            3
        };

        return a.Select(x => {
            if (x == 3) {
                throw new Exception();
            }
            return x;
        });
    }
}

Как сделать так что бы экспешен был перехвачен Exception фильтром? И пользователю не передавалось бы некорректных данных?

Answer 1

Для перехвата таких исключение надо использовать middleware. Но даже если вы его перехватите у вас все равно будет мало возможности, что то сделать. Дело в том, что это исключение возникает когда движок Asp уже начал писать в поток HttpResponse. А это означает, что все заголовки уже записаны и свойство HttpResponse.HasStarted взведено. И единственое что остается этот писать в конец тела ответа.

Как решить просто:

  • Использовать ToList()/ToArray().
  • Если исключение возникает на пример при подключении к базе, то попытаться открыть подключение за ранние.

Как решить сложно:

Можно подсунуть свой стрим в тело HttpResponse. Это довольно не стандартный путь и его стоит использовать ну если совсем припекло. Вдобавок метод конечно не эффективен в плане производительности, не позволяет переписывать заголовки и не очень красив, но работает.

class ExceptionHandlingMiddleware
{
    RequestDelegate _next;
    public ExceptionHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext context)
    {
        var originStream = context.Response.Body;
        try {
            using (var tmpStream = new MemoryStream()) {
                try {
                    context.Response.Body = tmpStream;
                    await _next(context);
                }
                catch (Exception e) {
                    tmpStream.SetLength(0);
                    var bytes = Encoding.UTF8.GetBytes(e.Message);
                    tmpStream.Write(bytes, 0, bytes.Length);
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                }
                tmpStream.Position = 0;
                await tmpStream.CopyToAsync(originStream);
            }
        }
        finally {
            context.Response.Body = originStream;
        }
    }        
}
READ ALSO
Как заполнить поля на сайте для POST запроса

Как заполнить поля на сайте для POST запроса

На сайте есть два поля-календаря, которыми задается интервал дат, за который необходимо получить информацию из БДНикак не могу понять, каким...

78
Как сохранять ответы пользователей на открытые вопросы анкеты (заранее не подготовленные)?

Как сохранять ответы пользователей на открытые вопросы анкеты (заранее не подготовленные)?

Проектирую MySQL базу данных для хранения анкет и ответов пользователей на вопросы этих анкетНа основе данных из базы формируется JSON

92
Помогите грамотно составить SQL запрос

Помогите грамотно составить SQL запрос

В общем имеется одна таблица, которая собирает поисковые запросы на сайтеВ таблице есть поле самого запроса и ИД соеденения

107
Обфускация личных данных в MySQL

Обфускация личных данных в MySQL

Подскажите как решить задачку с обезличиванием личных данных клиентов в базе MySQLМоя задача состоит в том что бы при бекапе были обезличены...

95