Как обрабатывать метод "ОПЦИИ" в ASP.NET MVC

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

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

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

ОПЦИИ /GetInTouch/CommunicateCard HTTP/1.1
  Хост: webservice.example.com
  Referer: http://192.168.5.206/  Метод доступа-контроля-запроса: POST
  Происхождение: http://192.168.5.206
  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/534.24 (KHTML, например, Gecko) Chrome/11.0.696.71 Safari/534.24
  Access-Control-Request-Headers: X-Requested-With, Content-Type
  Принять:/
  Accept-Encoding: gzip, deflate, sdch
  Accept-язык: 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 valid return true
        // else return false
        // currently returning NULL cuz I don't care at this point.
        return this.Jsonp(null);
    }

Ответ 1

Оказывается, мне нужно было создать 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);
        }
    }
}

Ответ 2

Я решил это по-другому в 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 (решения приветствуются), но нужно сделать барбекю и решение выше работает!

Ответ 3

Я добавил следующее в раздел <system.webServer> config:

<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>

Ответ 4

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

Ответ 5

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