Обработка 100-продолжить при перенаправлении запроса POST от контроллера веб-API
У меня есть ApiController
который отвечает на запрос POST, перенаправляя его через код состояния HTTP 307. Он делает это, используя только информацию из заголовка, поэтому тело запроса не требуется для этого действия. Это действие эквивалентно:
public HttpResponseMessage Post() {
var url;
// Some logic to construct the URL
var response = new HttpResponseMessage(HttpStatusCode.TemporaryRedirect);
response.Headers.Location = new System.Uri(url);
return response;
}
Это достаточно просто, но есть одно улучшение, которое я хотел бы сделать. Тело запроса может содержать большой объем данных, поэтому я хотел бы использовать код состояния HTTP 100, чтобы сделать этот запрос более эффективным. С контроллером, как сейчас, разговор может выглядеть так:
> POST /api/test HTTP/1.1
> Expect: 100-continue
> ...
< HTTP/1.1 100 Continue
> (request body is sent)
< HTTP/1.1 307 Temporary Redirect
< Location: (the URL)
< ...
Поскольку тело запроса не требуется для действия перенаправления, я хотел бы иметь возможность сократить разговор до:
> POST /api/controller HTTP/1.1
> Expect: 100-continue
> ...
< HTTP/1.1 307 Temporary Redirect
< Location: (the URL)
< ...
Большую часть дня я потратил на изучение того, как этого добиться, и не смог найти решение. В своем исследовании я узнал:
- Когда
ApiController
действие выполняется, то100 Continue
уже отправлено. - Когда
ApiController
построен,100 Continue
уже отправлено. - Когда
HttpApplication
"sPreRequestHandlerExecute
событие срабатывает,100 Continue
ответ не был отправлен. - Когда
DelegatingHandler
выполняет,100 Continue
уже отправлено.
Исходя из этого, лучшее решение, которое я нашел до сих пор, - это создать HttpModule
который использует RouteData
на RequestContext
переопределить ответ, когда ApiController
речь идет о получателе запроса. Однако это далеко от идеального решения по нескольким причинам (разделение кода, отсутствие использования привязки параметров Web API и обход дополнительной логики в AuthorizeAttribute
на ApiController
).
Кажется, что должно быть лучшее решение для этого, но я нашел очень мало информации о том, как правильно обращаться с Expect: 100-continue
заголовок в приложении веб-API. Какой бы самый простой способ реализовать это ApiController
правильно обращаться с Expect: 100-continue
заголовок?
2 ответа
... Вы уверены, что вам нужна эта оптимизация?
Если вы используете IIS 6, вам нужно перейти в режим совместимости с IIS 5 и написать фильтр ISAPI ReadRawData / SendRawData. Расширение ISAPI не может быть рассмотрено по причинам, изложенным ниже в моем ответе. (Если вы используете IIS 5 или ниже, пусть Бог помилует вашу душу)
Если вы используете IIS 7+, возможно, вам удастся написать собственный модуль IIS.
Вы правы, полагая, что к тому времени, когда контроллер включится, ответ уже будет отправлен, потому что Web API живет внутри ASP.NET; этот ответ обрабатывается ядром IIS.
Некоторый легкий материал для чтения
Дэвид Ван
"100 continue", например "400 Bad Request" или "Kernel Response Cache Hit", является особенным в том смысле, что HTTP.SYS прозрачно обрабатывает его в режиме ядра, не уведомляя пользовательский режим ни о чем. вывод - они могут только генерировать вывод ответа, но не видеть результаты вывода ответа. Таким образом, расширение ISAPI никогда не сможет взаимодействовать с запросами, которые генерируют ответы "100 продолжить" или сами "100 продолжить" для их подавления.
На IIS6 единственный способ внедрить обработку в пользовательском режиме в эти прозрачные обработки запросов HTTP.SYS - это запустить в режиме совместимости IIS5 и использовать фильтр ISAPI ReadRawData / SendRawData. ReadRawData заставляет HTTP.SYS пересылать необработанные данные из сети в пользовательский режим для фильтрации PRIOR для анализа выходных данных пользовательского режима в HTTP-запросах для помещения в очереди.
Конечно, этот метод полностью отрицает цель запуска IIS6 с пулами приложений и изоляцией процесса (один сбой в этом процессе фильтрации пользовательского режима останавливает весь сервер)... но таков компромисс на стороне сервера, когда клиент глючит...
К вашему сведению: этот подход не будет работать на Vista Server/IIS7. HTTP.SYS больше не будет передавать необработанные данные из сети в пользовательский режим для фильтрации перед синтаксическим анализом, поэтому для кода пользовательского режима будет невозможно узнать, что произошел запрос, который запускает автоматическое "100 продолжить".
редактировать
Я предполагаю, что вы перенаправляете браузер на другой контроллер в том же решении. Вместо перенаправления вы можете обработать запрос непосредственно в исходном URL-адресе. Таким образом, клиент должен будет отправить запрос только один раз, не перенаправляя его вообще.
Одним из способов реализации этого было бы написать свой собственный IHttpControllerSelector
который назначит запрос правильному контроллеру на основе заголовков запроса.
Возможно, вы захотите проверить следующий SO-вопрос, если хотите назначить настраиваемый селектор только для определенного маршрута: настраиваемый IHttpControllerSelector веб-API ASP.NET для отдельного маршрута