Начало работы с CORS
По умолчанию в целях безопасности браузер ограничивает ajax-запросы между различными доменами. Однако нередко возникает ситуация, когда необходимо выполнять запросы из приложения с одного адреса (или домена) к приложению, которое размещено по другому адресу. Для этого нам надо использовать технику, которая называется CORS (Cross Origin Resource Sharing).
В начале создадим новый проект ASP.NET Core по типу Empty:
Этот проект будет получать запросы от других приложений и отправлять им ответ. Для этого изменим его класс Startup следующим образом:
Для работы с CORS необходим пакет Microsoft.AspNetCore.Cors . В проекте ASP.NET Core 2.0 этот пакет уже имеется по умолчанию.
Для подключения сервисов CORS в приложение в методе ConfigureServices вызывается метод services.AddCors() .
Чтобы задействовать CORS для обработки запроса в методе Configure вызывается метод UseCors() . Для настройки конфигурации этот метод использует делегат, в который передается объект CorsPolicyBuilder . И с помощью этого объекта можно выполнить настройку CORS.
С помощью метода AllowAnyOrigin() мы указываем, что приложение может обрабатывать запросы от приложений по любым адресам.
В итоге всем подключенным клиентам данное приложение будет отправлять строку "Hello World!".
Для тестирования создадим второй проект также ASP.NET Core 2.0 по типу Empty. Определим во втором проекте следующий класс Startup:
Здесь мы будем работать только со статическими файлами. В частности, определим во втором проекте в папке wwwroot html-страницу index.html , которая будет загружаться по умолчанию:
В данном случае по нажатию на кнопку будет выполняться ajax-запрос к первому приложению, который, в моем случае, будет запускаться по адресу "http://localhost:49352/". Полученный от первого приложения ответ будет загружаться в блок
Запустим оба проекта и во втором приложении нажмем на кнопку, чтобы получить ответ от первого приложения:
Кроме тех данных, которые передаются через параметры метода, в контроллере мы можем получить различную информацию, связанную с контекстом контроллера, в том числе контекст запроса и все его данные. Для получения контекста в контроллере нам доступно свойство ControllerContext , которое представляет одноименный класс ControllerContext. Этот класс определяет ряд важный свойств:
HttpContext : содержит информацию о контексте запроса
ActionDescriptor : возвращает дескриптор действия — объект ActionDescriptor, который описывает вызываемое действие контроллера
ModelState : возвращает словарь ModelStateDictionary, который используется для валидации данных, отправленных пользователем
RouteData : возвращает данные маршрута
Для получения информации о запросе нас прежде всего будет интересовать свойство HttpContext, которое представляет объект Microsoft.AspNetCore.Http.HttpContext . Это свойство также доступно через свойство HttpContext в контроллере. То есть следующие вызовы будут обращаться к одному и тому же объекту:
Объект HttpContext инкапсулирует всю информацию о запросе. В частности, он определяет следующие свойства:
Request : содержит собственно информацию о текущем запросе.
Response : управляет ответом
User : представляет текущего пользователя, который обращается к приложению
Session : объект для работы с сессиями
Request
Свойство HttpContext.Request представляет объект HttpRequest и предоставляет разнообразную информацию о запросе. Этот же объект доступен через свойство Request класса Conroller. Среди свойств объекта Request можно выделить следующие:
Body : объект Stream, который используетя для чтения данных запроса
Cookies : куки, полученные в запросе
Form : коллекция значений отправленных форм
Headers : коллекция заголовков запроса
Path : возвращает запрошенный путь — строка запроса без домена и порта
Query : возвращает коллекцию переданных через строку запроса параметров
QueryString : возвращает ту часть запроса, которая содержит параметры. Например, в запросе http://localhost:52682/Home/Index?alt=4 это будет ?alt=4
Вся основная информация нам доступна из заголовков. Например, получим все имеющиеся заголовки и выведем их в браузере:
Получим значения определенных заголовков:
Response
Свойство HttpContext.Response представляет объект HttpResponse и позволяет управлять ответом на запрос, в частности, устанавливать заголовки ответа, куки, отправлять в выходной поток некоторый ответ. Этот же объект доступен через свойство Response класса Conroller. Среди свойств объекта Response можно выделить следующие:
Body : объект Stream, который применяется для отправки данных в ответ пользователю
Cookies : куки, отправляемые в ответе
ContentType : MIME-тип ответа
Headers : коллекция заголовков ответа
StatusCode : статусный код ответа
Также с помощью объекта Response мы можем отправить ответ клиенту с помощью метода WriteAsync() :
I’m trying to read the request body in OnActionExecuting method, but I always get null for the body.
I have tried to explicitly set the stream position to 0 but that also didn’t work. Since this is ASP.NET CORE, Things are little different I think. I can see all the samples here referring to old webapi versions.
Is there any other way of doing this?
6 Answers 6
In ASP.Net Core it seems complicated to read several times the body request, however if your first attempt does it the right way, you should be fine for the next attempts.
I read several turnaround for example by substituting the body stream, but I think the following is the cleanest:
The most important points being
- to let the request know that you will read its body twice or more times,
- to not close the body stream, and
- to rewind it to its initial position so the internal process does not get lost.
As pointed out by Murad, you may also take advantage of the .Net Core 2.1 extension: EnableBuffering It stores large requests onto the disk instead of keeping it in memory, avoiding large-streams issues stored in memory (files, images, . ). You can change the temporary folder by setting ASPNETCORE_TEMP environment variable, and files are deleted once the request is over.
In an AuthorizationFilter, you can do the following:
Then you can use the body again in the request handler.
In your case if you get a null result, it probably means that the body has already been read at an earlier stage. In that case you may need to use a middleware (see below).
However be careful if you handle large streams, that behavior implies that everything is loaded into memory, this should not be triggered in case of a file upload.
You may want to use this as a Middleware
Mine looks like this (again, if you download/upload large files, this should be disabled to avoid memory issues):