Изменение объекта ответа из ПО промежуточного слоя OWIN

Мое промежуточное ПО OWIN выглядит так. (Framework - это веб-API ASP.NET).

public class MyMiddleware : OwinMiddleware
{
    public MyMiddleware(OwinMiddleware next) : base(next) { }

    public override async Task Invoke(OwinRequest request, OwinResponse response)
    {
        var header = request.GetHeader("X-Whatever-Header");

        await Next.Invoke(request, response);

        response.SetHeader("X-MyResponse-Header", "Some Value");
        response.StatusCode = 403;

    }
}

Вопросы:

  1. Рекомендуется ли использовать OwinMiddleware? Я вижу, что в исходном коде Katana некоторые классы промежуточного программного обеспечения являются производными от OwinMiddleware, а некоторые - нет.

  2. Я могу видеть заголовки запросов. Установка заголовка ответа или кода состояния после Next.Invoke в моем промежуточном программном обеспечении не влияет на ответ, возвращаемый клиенту. Но если я устанавливаю заголовок ответа или статус перед вызовом Next.Invoke, ответ с заголовками и статусом, который я установил, возвращается клиенту. Как правильно их настроить?


person Badri    schedule 07.07.2013    source источник


Ответы (3)


  1. Да, рекомендуется использовать OwinMiddleware. Причина, по которой некоторые классы промежуточного программного обеспечения не являются производными от OwinMiddleware, заключается в том, что они либо еще не переключились, потому что класс был представлен недавно. Или чтобы сборка по какой-то причине не зависела от сборки Microsoft.Owin.

  2. Вероятная причина, по которой установка материала в ответе после вызова Invoke on Next не работает, заключается в том, что HTTP-заголовок ответа отправляется, как только кто-либо начинает писать в поток тела ответа. Таким образом, любые изменения кода состояния или заголовков HTTP после того, как компонент промежуточного программного обеспечения начинает писать в тело ответа, не окажут никакого эффекта.

Вы можете попробовать использовать обратный вызов OnSendingHeaders, предоставляемый OWIN. Вот как это можно использовать:

public override async Task Invoke(IOwinContext context)
{
   var response = context.Response;
   var request =  context.Request;

   response.OnSendingHeaders(state =>
   {
       var resp = (OwinResponse)state;
       resp.Headers.Add("X-MyResponse-Header", "Some Value");
       resp.StatusCode = 403;
       resp.ReasonPhrase = "Forbidden";
    }, response);

  var header = request.Headers["X-Whatever-Header"];

  await Next.Invoke(context);
}

Благодарим biscuit314 за обновление моего ответа.

person Youssef Moussaoui    schedule 07.07.2013
comment
Спасибо, Юсеф. Что касается пункта 2, есть ли в исходном коде Katana или где-то еще пример кода, на который я могу взглянуть с точки зрения наилучшей практики? Я считаю, что то, о чем я прошу, является довольно распространенной потребностью, и каждый ли должен делать это переключение потока только для установки заголовка ответа, если мы используем промежуточное ПО OWIN? Будем признательны за любые указатели. - person Badri; 07.07.2013
comment
Я могу решить проблему, следуя вашему предложению, но нет ли простого выхода? Я имею в виду, что это слишком большая работа, чтобы просто установить заголовок ответа на выходе, особенно если он используется для обработчика сообщений веб-API. - person Badri; 07.07.2013
comment
Обновил ответ более простым методом. Пожалуйста, дайте мне знать, если это сработает. - person Youssef Moussaoui; 08.07.2013
comment
Да, это намного лучше. Это именно то, что я ищу. Спасибо. - person Badri; 09.07.2013
comment
OnSendingHeaders больше нет? или мне не хватает ссылок? - person Poul K. Sørensen; 12.09.2013
comment
@pksorensen - OnSendingHeaders находится в объекте ответа, а не в запросе. Я отправил правку. - person biscuit314; 02.03.2014
comment
Да, рекомендуется использовать OwinMiddleware. Нет, это не так. github.com/owin/owin/issues/20 github.com/owin/owin/issues/19 - person ; 19.03.2014
comment
@YoussefMoussaoui, а когда мне нужно write что-то другое (как правило, совершенно другой ответ) в зависимости от того, что next сделал с ответом? - person Mo Valipour; 22.05.2015
comment
@Valipour, обратный вызов OnSendingHeaders запускается только после того, как данные начинают отправляться по сети, поэтому вы должны иметь возможность проверить ответ в этот момент. - person Youssef Moussaoui; 02.06.2015
comment
спасибо, я действительно делаю это и могу подтвердить, что работает отлично. ОДНАКО вы должны выполнять перезапись кода синхронно, иначе в конечном итоге возникнут проблемы с памятью. Мой код можно увидеть здесь: mvalipour. github.io/back-end/2015/05/30/ - person Mo Valipour; 02.06.2015
comment
Я пришел к этому после того, как потратил день безрезультатно, пытаясь выяснить, почему мое промежуточное программное обеспечение не устанавливает файлы cookie, о которых я ему говорил. Спасибо за это! - person Aashish Koirala; 31.03.2017
comment
Изменилась ли эта функция в последнее время? Я не вижу никаких изменений в заголовках моих ответов при таком использовании. - person gooram; 18.06.2021

Я попытался отредактировать отличный ответ Юсефа, чтобы исправить небольшую ошибку и обновить пример с тем, как теперь работает OwinMiddleware.

Редактирование было отклонено (ну, одобрено одним, отклонено одним из-за того, что оно слишком незначительное, и отклонено двумя из-за того, что оно слишком большое).

Вот эта версия кода Юсефа:

public override async Task Invoke(IOwinContext context)
{
  var response = context.Response;
  var request =  context.Request;

  response.OnSendingHeaders(state =>
    {
        var resp = (OwinResponse)state;
        resp.Headers.Add("X-MyResponse-Header", "Some Value");
        resp.StatusCode = 403;
        resp.ReasonPhrase = "Forbidden"; // if you're going to change the status code
                                         // you probably should also change the reason phrase
    }, response);

  var header = request.Headers["X-Whatever-Header"];

  await Next.Invoke(context);
}
person biscuit314    schedule 02.03.2014

Я использовал этот код, чтобы узнать время, затрачиваемое на каждый запрос.

appBuilder.Use(async (context, next) =>
        {
            var watch = new Stopwatch();
            watch.Start();
            await next();
            watch.Stop();
            context.Response.Headers.Set("ResponseTime", watch.Elapsed.Seconds.ToString());
        });
person Deependra Kushwah    schedule 31.01.2020