Как поддерживать глагол HTTP OPTIONS в приложении ASP.NET MVC / WebAPI

56

Я установил веб-приложение ASP.NET, начиная с шаблона MVC 4/Web API. Кажется, что все работает очень хорошо - никаких проблем, о которых я знаю. Я использовал Chrome и Firefox, чтобы пройти через сайт. Я тестировал использование Fiddler, и все ответы, похоже, были на деньги.

Итак, теперь я перехожу к написанию простого Test.aspx, чтобы использовать этот новый веб-API. Соответствующие части script:

<script type="text/javascript">
    $(function () {

        $.ajax({
            url: "http://mywebapidomain.com/api/user",
            type: "GET",
            contentType: "json",
            success: function (data) {

                $.each(data, function (index, item) {

                    ....

                    });
                }
                );

            },
            failure: function (result) {
                alert(result.d);
            },

            error: function (XMLHttpRequest, textStatus, errorThrown) {
                alert("An error occurred, please try again. " + textStatus);
            }

        });

    });
</script>

Это генерирует заголовок REQUEST:

OPTIONS http://host.mywebapidomain.com/api/user HTTP/1.1
Host: host.mywebapidomain.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://mywebapidomain.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type
Connection: keep-alive

Как и в случае с веб-API, метод 405 не допускается.

HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Sep 2013 13:28:12 GMT
Content-Length: 96

<Error><Message>The requested resource does not support http method 'OPTIONS'.</Message></Error>

Я понимаю, что глагол OPTIONS по умолчанию не подключен в контроллерах веб-API... Итак, я поместил следующий код в свой UserController.cs:

// OPTIONS http-verb handler
public HttpResponseMessage OptionsUser()
{
    var response = new HttpResponseMessage();
    response.StatusCode = HttpStatusCode.OK;
    return response;
}

... и это устранило ошибку 405 Method Not Allowed, но ответ полностью пуст - данные не возвращаются:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Sep 2013 12:56:21 GMT
Content-Length: 0

Должна быть дополнительная логика... Я не знаю, как правильно закодировать метод "Параметры", или если контроллер - это даже подходящее место для размещения кода. Странно (мне), что сайт веб-API правильно реагирует при просмотре с Firefox или Chrome, но ошибка .ajax выше ошибок. Как я могу обработать проверку "предполетного" кода .ajax? Может быть, мне стоит решить эту проблему на стороне клиента. Или, если это проблема на стороне сервера из-за отсутствия обработки глагола OPTIONS.

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

UPDATE IMHO, это проблема с клиентской стороной и связана с кодом Ajax JQuery выше. Я говорю это, потому что Fiddler не показывает никаких заголовков ошибок 405, когда я обращаюсь к mywebapidomain/api/user из веб-браузера. Единственное место, где я могу дублировать эту проблему, - это вызов JQuery.ajax(). Кроме того, идентичный вызов Ajax выше работает нормально при запуске на сервере (тот же домен).

Я нашел еще одно сообщение: Прототип запроса AJAX, отправляемого как ОПЦИИ, а не GET; приводит к ошибке 501, которая, по-видимому, связана, но я не без труда реализовал свои предложения. По-видимому, JQuery закодирован так, что если запрос Ajax является кросс-доменом (это мой), он добавляет пару заголовков, которые каким-то образом запускают заголовок OPTIONS.

'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,

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

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

  • 0
    что вы собираетесь отправить с вашим запросом опций?
  • 0
    Мне вообще не нужно отправлять запрос ОПЦИИ. По какой-то причине это делается, когда Ajax-вызов выполняется между доменами. Итак, как вы можете видеть в Javascript, все, что я делаю, задает GET, но заголовок OPTIONS отправляется по протоколу HTTP. Это проверка перед полетом.
Показать ещё 4 комментария
Теги:
asp.net-web-api

10 ответов

35
Лучший ответ

Как отметил в своем комментарии Daniel A. White, запрос OPTIONS, скорее всего, создается клиентом как часть запроса JavaScript для перекрестного домена. Это делается автоматически с помощью совместимых браузеров Cross Origin Resource Sharing (CORS). Запрос представляет собой предварительный или предполетный запрос, сделанный до фактического запроса AJAX, чтобы определить, какие глаголы запроса и заголовки поддерживаются для CORS. Сервер может выбрать поддержку для него, ни для одного, ни для некоторых из HTTP-глаголов.

Чтобы завершить изображение, в запросе AJAX имеется дополнительный заголовок "Origin", в котором указано, где была загружена исходная страница, на которой размещается JavaScript. Сервер может выбрать поддержку запроса из любого источника или только для набора известных, надежных источников. Разрешение любого происхождения является угрозой безопасности, поскольку может увеличить риск получения запроса на перекрестную площадку (CSRF).

Итак, вам нужно включить CORS.

Вот ссылка, которая объясняет, как это сделать в ASP.Net Web API

http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api#enable-cors

Реализация, описанная там, позволяет вам указать, среди прочего,

  • Поддержка CORS для каждого действия, для каждого контроллера или глобальной базы.
  • Поддерживаемые источники
  • При включении CORS - контроллера или глобального уровня поддерживаемые HTTP-глаголы
  • Поддерживает ли сервер отправку учетных данных с запросами на кросс-начало.

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

Что касается браузеров, поддерживающих CORS, Wikipedia говорит, что следующие двигатели поддерживают его:

  • Gecko 1.9.1 (FireFox 3.5)
  • WebKit (Safari 4, Chrome 3)
  • MSHTML/Trident 6 (IE10) с частичной поддержкой в ​​IE8 и 9
  • Presto (Opera 12)

http://en.wikipedia.org/wiki/Cross-origin_resource_sharing#Browser_support

  • 0
    Привет Майк. Спасибо за ссылку, вот еще одна, которая также хороша: codeguru.com/csharp/.net/net_asp/… - хотя ни одна из них еще не устранила проблему для меня, пока. Я разместил свои тестовые страницы на сервере, и это помогает мне в краткосрочной перспективе. Я попытался установить Microsoft.AspNet.WebApi.Cors, но получил странную ошибку, что у моего приложения не было никаких зависимостей WebApi, поэтому установка откатилась. Спасибо за ваш ответ - я знаю, что это правильно. +1!
  • 0
    +1 - просто билет для того, чтобы я начал работать, а также отправная точка для получения дополнительной информации о web.api.
Показать ещё 2 комментария
81

Ответ Майка Гудвина велик, но, когда я это пробовал, казалось, что он был нацелен на MVC5/WebApi 2.1. Зависимости для Microsoft.AspNet.WebApi.Cors не играли хорошо с моим проектом MVC4.

Самый простой способ включить CORS на WebApi с MVC4 был следующим.

Обратите внимание, что я разрешил все, я предлагаю вам ограничить Origin только теми клиентами, которым вы хотите, чтобы ваш API работал. Предотвращение всех угроз безопасности.

Web.config:

<system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
      </customHeaders>
    </httpProtocol>
</system.webServer>

BaseApiController.cs:

Мы делаем это, чтобы разрешить HTTP-глаголы OPTIONS

 public class BaseApiController : ApiController
  {
    public HttpResponseMessage Options()
    {
      return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
    }
  }
  • 0
    @Castaldi это, вероятно, потому что ответ был нацелен на WebApi 1, у которого не было маршрутизации атрибутов. Для WebApi 2 я бы предложил использовать пакет Microsoft CORS nuget. nuget.org/packages/Microsoft.AspNet.WebApi.Cors
  • 0
    Вы также можете использовать [ApiExplorerSettings (IgnoreApi = true)], чтобы игнорировать конечные точки OPTIONS на Swagger.
Показать ещё 2 комментария
17

Просто добавьте это в свой метод Application_OnBeginRequest (это позволит поддерживать CORS во всем мире для вашего приложения) и "обрабатывать" предполетные запросы:

var res = HttpContext.Current.Response;
var req = HttpContext.Current.Request;
res.AppendHeader("Access-Control-Allow-Origin", req.Headers["Origin"]);
res.AppendHeader("Access-Control-Allow-Credentials", "true");
res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");

// ==== Respond to the OPTIONS verb =====
if (req.HttpMethod == "OPTIONS")
{
    res.StatusCode = 200;
    res.End();
}

* security: имейте в виду, что это позволит отправлять запросы ajax из любого места на ваш сервер (вместо этого вы можете разрешать только список источников/URL-адресов, разделенных запятыми).

Я использовал текущее происхождение клиента вместо *, потому что это позволит установить учетные данные = > установка Access-Control-Allow-Credentials в значение true, чтобы разрешить управление сеансом браузера

также вам нужно включить в свой раздел webconfig system.webServer удаление и установку, исправление и варианты глаголов, в противном случае IIS заблокирует их:

<handlers>
  <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
  <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>

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

  • 3
    Благодарю. Только этот Application_OnBeginRequest помог мне. Но если вы хотите иметь возможность также получать данные с авторизацией, вы должны также добавить Authorization в Access-Control-Allow-Headers
7

Мне удалось преодолеть 405 и 404 ошибки, которые были брошены на предполетные запросы параметров ajax только по специальному коду в global.asax

protected void Application_BeginRequest()
    {            
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
        if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
        {
            //These headers are handling the "pre-flight" OPTIONS call sent by the browser
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
            HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
            HttpContext.Current.Response.End();
        }
    }

PS: Рассмотрите проблемы безопасности при разрешении всего *.

Мне пришлось отключить CORS, поскольку он возвращал заголовок "Access-Control-Allow-Origin", содержащий несколько значений.

Также необходимо это в web.config:

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
  <remove name="OPTIONSVerbHandler"/>
  <remove name="TRACEVerbHandler"/>
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>

И app.pool необходимо установить в интегрированный режим.

7

У меня была такая же проблема. Для меня было исправлено удаление настраиваемого типа контента из вызова jQuery AJAX. Пользовательские типы контента вызывают запрос перед полетом. Я нашел это:

Браузер может пропустить запрос перед полетом, если выполняются следующие условия:

Метод запроса: GET, HEAD или POST, и

Приложение не устанавливает заголовки запросов, отличные от Accept, Accept-Language, Content-Language, Content-Type или Last-Event-ID, и

Заголовок Content-Type (если установлен) является одним из следующих:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

С этой страницы: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api (в разделе "Запросы предполетной проверки" )

4

После того, как вы столкнулись с одной и той же проблемой в проекте Web API 2 (и не можете использовать стандартные пакеты CORS по причинам, не стоящим здесь), я смог решить это, выполнив собственный DelagatingHandler:

public class AllowOptionsHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        if (request.Method == HttpMethod.Options &&
            response.StatusCode == HttpStatusCode.MethodNotAllowed)
        {
            response = new HttpResponseMessage(HttpStatusCode.OK);
        }

        return response;
    }
}

Для конфигурации веб-API:

config.MessageHandlers.Add(new AllowOptionsHandler());

Обратите внимание, что у меня также есть заголовки CORS, включенные в Web.config, похожие на некоторые из других ответов, размещенных здесь:

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
    <remove name="WebDAVModule" />
  </modules>

  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="*" />
      <add name="Access-Control-Allow-Headers" value="accept, cache-control, content-type, authorization" />
      <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
    </customHeaders>
  </httpProtocol>

  <handlers>
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
    <remove name="TRACEVerbHandler" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  </handlers>
</system.webServer>

Обратите внимание, что мой проект не включает MVC, а только Web API 2.

  • 1
    Наконец решение для моих 3 бессонных ночей!
3

В ASP.NET web api 2 добавлена ​​поддержка CORS. Проверьте ссылку [http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api]

1

У меня была такая же проблема, и вот как я ее исправил:

Просто бросьте это в свой web.config:

<system.webServer>
    <modules>
      <remove name="WebDAVModule" />
    </modules>

    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Expose-Headers " value="WWW-Authenticate"/>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" />
        <add name="Access-Control-Allow-Headers" value="accept, authorization, Content-Type" />
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>

    <handlers>
      <remove name="WebDAV" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>
1
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" )
        {
            Response.Clear();
            Response.StatusCode = 200;
            Response.End();
        }
    }
1

Я тоже столкнулся с той же проблемой.

Следуйте приведенному ниже шагу, чтобы решить проблему (CORS) в браузерах.

Включите REDRock в ваше решение с помощью ссылки Cors. Включите ссылку WebActivatorEx в решение Web API.

Затем добавьте файл CorsConfig в папку App_Start веб-API.

[assembly: PreApplicationStartMethod(typeof(WebApiNamespace.CorsConfig), "PreStart")]

namespace WebApiNamespace
{
    public static class CorsConfig
    {
        public static void PreStart()
        {
            GlobalConfiguration.Configuration.MessageHandlers.Add(new RedRocket.WebApi.Cors.CorsHandler());
        }
    }
}

С этими изменениями я смог получить доступ к webapi во всех браузерах.

  • 3
    Что такое Redrock? Я выполнил поиск Google и пакет Nuget, но ничего не вернулось. Ссылка была бы хорошей.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню