как обрабатывать «метод OPTIONS» в ASP.NET MVC - PullRequest
13 голосов
/ 09 августа 2011

Приложение Sencha Touch отправляет форму на мой WebService, но вместо отправки POST отправляет OPTIONS.

Ячитая похожую ветку здесь , но я просто не знаю, как обработать метод OPTIONS в моем коде.

Я попытался добавить атрибут [AllowAjax] вМое действие, однако оно не существует в MVC3.

ОПЦИИ / GetInTouch / CommunicateCard HTTP / 1.1
Host: webservice.example.com
Referer: http://192.168.5.206/ Метод контроля доступа-запроса: POST
Источник: http://192.168.5.206
Пользовательский агент: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_7_0)AppleWebKit / 534.24 (KHTML, как Gecko) Chrome / 11.0.696.71 Safari / 534.24
Заголовки запросов-контроля доступа: X-Requested-With, Content-Type
Принимать: /
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US, en; q = 0,8
Accept-Charset: ISO-8859-1, utf-8; q = 0,7, *;q = 0.3

В моем ActionMethod ям, используя следующий код.

    public JsonpResult CommunicateCard(CommunicateCard communicateCard)
    {

        // Instantiate a new instance of MailMessage
        MailMessage mMailMessage = new MailMessage();

        // removed for security/brevity

        // Set the body of the mail message
        mMailMessage.Body = communicateCard.name; // THIS IS CURRENTLY BLANK :-(

        // removed for security/brevity
        mSmtpClient.Send(mMailMessage);

        // do server side validation on form input
        // if it's valid return true
        // else return false
        // currently returning NULL cuz I don't care at this point.
        return this.Jsonp(null);
    }

Ответы [ 6 ]

12 голосов
/ 10 августа 2011

Оказывается, мне пришлось создать ActionFilterAttribute

namespace WebService.Attributes
{
    public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            HttpContext.Current.Response.Cache.SetNoStore();

            filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", "*");

            string rqstMethod = HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
            if (rqstMethod == "OPTIONS" || rqstMethod == "POST")
            {
                filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
                filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Headers", "X-Requested-With, Accept, Access-Control-Allow-Origin, Content-Type");
            }
            base.OnActionExecuting(filterContext);
        }
    }
}
7 голосов
/ 10 августа 2012

Я решил это по-другому в MVC и IIS.Причина, по которой я обнаружил эту проблему, заключалась в том, что я хотел POST-данные из клиентского javascript (для которого JSONP не работает), и, кроме того, хотел разрешить JSON-данные, которые находятся внутри содержимого запроса POST.

На самом деле ваш код хочет игнорировать первый запрос CORS OPTIONS, так как это, вероятно, будет «настройка для всего сайта», а не для настройки вызова по API.

Сначала я настроил IIS для отправки ответа CORS. Это можно сделать через диспетчер IIS (или через обновления web.config). Если вы используете IIS, перейдите на сайт, на котором вы хотите добавить эти два значения:

  • Access-Control-Allow-Origin до "*" (для тестирования, для большей безопасности вы можете захотеть ограничить его определенными вызывающими доменами)
  • Access-Control-Allow-Headers, «Content-Type, Accept» (это для публикации данных JSON)

Затем я создал собственный ActionFilter, который должен применяться для каждого контроллера, который вы хотите принимать POST-данные, что можетвызвать запрос CORS.Фильтр настраиваемых действий был:

public class CORSActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.HttpMethod == "OPTIONS")
        {
            // do nothing let IIS deal with reply!
            filterContext.Result = new EmptyResult();
        }
        else
        {
            base.OnActionExecuting(filterContext);
        }
    }
}

Затем в начале каждого контроллера вам нужно применить это для добавления в атрибут, например:

[CORSActionFilter]
public class DataSourcesController : Controller

Теперь я уверен, что естьспособ сделать это для всего решения MVC (решения приветствуются), но нужно сделать барбекю, и решение, указанное выше, работает!

5 голосов
/ 23 января 2013

Я добавил следующее в мой <system.webServer> раздел конфигурации:

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Headers" value="Content-Type, Accept, X-Requested-With"/>
    <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS"/>
    <add name="Access-Control-Allow-Origin" value="*"/>
  </customHeaders>
</httpProtocol>
3 голосов
/ 10 августа 2012

Просто для ответа на вопрос, почему «OPTIONS», а не «POST», то есть потому, что браузер реализует CORS ( Совместное использование ресурсов общего доступа ).Это процесс, состоящий из двух частей: сначала отправляется запрос OPTIONS, затем, если сервер отвечает с приемлемыми условиями, браузер ставит фактический запрос с данными / содержимым.

1 голос
/ 12 февраля 2019

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

Единственный способ, которым я мог сделать это, был с HttpModule.

Я подписался на эту запись в блоге:

http://geekswithblogs.net/abhijeetp/archive/2016/06/04/adding-cors-support-for-asp.net--webapi-the-no-hassle.aspx

Чтобы подвести итоги работы, это код:

namespace WebAPI.Infrastructure
{
    using System;
    using System.Web;
    using System.Collections;
    using System.Net;
    public class CrossOriginModule : IHttpModule
    {
        public String ModuleName
        {
            get { return "CrossOriginModule"; }
        }

        public void Init(HttpApplication application)
        {
            application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
        }

        private void Application_BeginRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            CrossOriginHandler.AddCorsResponseHeaders(context);
        }

        public void Dispose()
        {
        }
    }

    public class CrossOriginHandler : IHttpHandler
    {
        #region Data Members
        const string OPTIONS = "OPTIONS";
        const string PUT = "PUT";
        const string POST = "POST";
        const string PATCH = "PATCH";
        static string[] AllowedVerbs = new[] { OPTIONS, PUT, POST, PATCH };
        const string Origin = "Origin";
        const string AccessControlRequestMethod = "Access-Control-Request-Method";
        const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
        const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
        const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
        const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
        const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
        const string AccessControlMaxAge = "Access-Control-Max-Age";
        const string MaxAge = "86400";
        #endregion

        #region IHttpHandler Members
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            switch (context.Request.HttpMethod.ToUpper())
            {
                //Cross-Origin preflight request
                case OPTIONS:
                    AddCorsResponseHeaders(context);
                    break;

                default:
                    break;
            }
        }
        #endregion

        #region Static Methods
        public static void AddCorsResponseHeaders(HttpContext context)
        {
            if (Array.Exists(AllowedVerbs, av => string.Compare(context.Request.HttpMethod, av, true) == 0))
            {
                var request = context.Request;
                var response = context.Response;
                var originArray = request.Headers.GetValues(Origin);
                var accessControlRequestMethodArray = request.Headers.GetValues(AccessControlRequestMethod);
                var accessControlRequestHeadersArray = request.Headers.GetValues(AccessControlRequestHeaders);
                if (originArray != null &&
                    originArray.Length > 0)
                    response.AddHeader(AccessControlAllowOrigin, originArray[0]);
                response.AddHeader(AccessControlAllowCredentials, bool.TrueString.ToLower());

                if (accessControlRequestMethodArray != null &&
                    accessControlRequestMethodArray.Length > 0)
                {
                    string accessControlRequestMethod = accessControlRequestMethodArray[0];
                    if (!string.IsNullOrEmpty(accessControlRequestMethod))
                    {
                        response.AddHeader(AccessControlAllowMethods, accessControlRequestMethod);
                    }
                }
                if (accessControlRequestHeadersArray != null &&
                    accessControlRequestHeadersArray.Length > 0)
                {
                    string requestedHeaders = string.Join(", ", accessControlRequestHeadersArray);
                    if (!string.IsNullOrEmpty(requestedHeaders))
                    {
                        response.AddHeader(AccessControlAllowHeaders, requestedHeaders);
                    }
                }
            }
            if (context.Request.HttpMethod == OPTIONS)
            {
                context.Response.AddHeader(AccessControlMaxAge, MaxAge);
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                context.Response.End();
            }
        } 
        #endregion
    }
}

и добавьте их к web.config:

<system.webServer>  
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
      <add name="CrossOriginModule" preCondition="managedHandler" type="WebAPI.Infrastructure.CrossOriginModule, Your_Assembly_Name" />
    </modules>
    <handlers>
      <remove name="WebDAV"/>
      <remove name="OPTIONSVerbHandler"/>
      <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" />
      <add name="CrossOrigin" verb="OPTIONS" path="*" type="WebAPI.Infrastructure.CrossOriginHandler, Your_Assembly_Name" />
    </handlers>   
     <security>
       <authorization>
         <remove users="*" roles="" verbs=""/>
         <add accessType="Allow" users="*" verbs="GET,HEAD,POST,PUT,PATCH,DELETE,DEBUG"/>
       </authorization>
     <requestFiltering>
       <requestLimits maxAllowedContentLength="6000"/>
       <verbs>
         <remove verb="OPTIONS"/>
         <remove verb="PUT"/>
         <remove verb="PATCH"/>
         <remove verb="POST"/>         
         <remove verb="DELETE"/>
       </verbs>
     </requestFiltering>
   </security> 
  </system.webServer>

Это работает для Web API и MVC.

1 голос
/ 27 октября 2016

Я попробовал все ответы здесь, но никто не работал. В конце концов я понял, что браузеры будут считать проверку перед полетом неудачной, если она возвращает не 200. В моем случае IIS возвращал 404, даже с заголовками. Это потому, что у меня было 2 атрибута в моем методе контроллера - [HttpPost] и [HttpOptions]. По-видимому, это недопустимый механизм выражения нескольких глаголов. Вместо этого мне пришлось использовать этот атрибут: [AcceptVerbs (HttpVerbs.Options | HttpVerbs.Post)]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...