Como suportar verbo HTTP OPTIONS no aplicativo ASP.NET MVC / WebAPI

Eu configurei um aplicativo da Web ASP.NET começando com um modelo MVC 4 / Web API. Parece que as coisas estão funcionando muito bem – sem problemas que eu saiba. Eu usei o Chrome e o Firefox para percorrer o site. Eu testei usando o Fiddler e todas as respostas parecem estar no dinheiro.

Então agora vou escrever um Test.aspx simples para consumir essa nova API da Web. As partes relevantes do script:

 $(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); } }); });  

Isso gera um header 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 

Como é, a API da Web retorna um Método 405 não permitido.

 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 The requested resource does not support http method 'OPTIONS'. 

Eu entendo que o verbo OPTIONS não está conectado em controladores de API da Web por padrão … Então, coloquei o seguinte código no meu UserController.cs:

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

… e isso eliminou o erro 405 Method Not Allowed, mas a resposta está completamente vazia – nenhum dado é retornado:

 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 

Deve haver lógica adicional … Não sei como codificar adequadamente o método Options ou se o controlador é o local adequado para colocar o código. Estranho (para mim) que o site da API da Web responda corretamente quando visualizado a partir do Firefox ou do Chrome, mas a chamada .ajax acima está errada. Como faço para lidar com a verificação “preflight” no código .ajax? Talvez eu devesse estar abordando esse problema na lógica .ajax do lado do cliente? Ou, se isso é um problema no lado do servidor devido a não lidar com o verbo OPTIONS.

Alguém pode ajudar? Este deve ser um problema muito comum e peço desculpas se ele foi respondido aqui. Eu procurei, mas não encontrei nenhuma resposta que ajudasse.

UPDATE IMHO, este é um problema do lado do cliente e tem a ver com o código Ajax JQuery acima. Digo isso porque o Fiddler não mostra nenhum header de erro 405 quando access o mywebapidomain / api / user a partir de um navegador da web. O único local em que posso duplicar esse problema é a chamada .jajax () do JQuery. Além disso, a chamada idêntica do Ajax acima funciona bem quando executada no servidor (mesmo domínio).

Eu encontrei outro post: solicitação de protótipo AJAX sendo enviada como OPTIONS em vez de GET; resulta em 501 erro que parece estar relacionado, mas eu consertei com suas sugestões sem sucesso. Aparentemente, o JQuery é codificado de forma que, se uma requisição Ajax for cross domain (qual é a minha), ela adiciona alguns headers que acionam o header OPTIONS de alguma forma.

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

Parece que deve haver uma solução melhor disponível do que modificar o código principal no JQuery …

A resposta fornecida abaixo assume que isso é um problema do lado do servidor. Talvez, eu acho, mas eu me inclino para o cliente e chamar um provedor de hospedagem não vai ajudar.

Como Daniel A. White disse em seu comentário, a solicitação OPTIONS é provavelmente criada pelo cliente como parte de uma solicitação JavaScript de vários domínios. Isso é feito automaticamente pelos navegadores compatíveis com Cross Origin Resource Sharing (CORS). A solicitação é uma solicitação preliminar ou pré-flight , feita antes da solicitação real do AJAX para determinar quais verbos e headers de solicitação são suportados para o CORS. O servidor pode optar por suportá-lo para nenhum, todos ou alguns dos verbos HTTP.

Para completar a imagem, a solicitação AJAX tem um header “Origem” adicional, que identificou onde a página original que está hospedando o JavaScript foi exibida. O servidor pode optar por dar suporte a solicitações de qualquer origem ou apenas para um conjunto de origens conhecidas e confiáveis. Permitir qualquer origem é um risco de segurança, pois pode aumentar o risco de falsificação de solicitação entre sites (CSRF, Cross Site Request Forgery).

Então, você precisa ativar o CORS.

Aqui está um link que explica como fazer isso no ASP.Net Web API

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

A implementação descrita aí permite que você especifique, entre outras coisas

  • Suporte a CORS em uma base por ação, por controlador ou global
  • As origens suportadas
  • Ao ativar o controlador CORS aa ou nível global, os verbos HTTP suportados
  • Se o servidor suporta o envio de credenciais com solicitações de origem cruzada

Em geral, isso funciona bem, mas você precisa ter certeza de que está ciente dos riscos de segurança, especialmente se permitir solicitações de origem cruzada de qualquer domínio. Pense com muito cuidado antes de permitir isso.

Em termos de quais navegadores suportam o CORS, a Wikipedia diz que os seguintes mecanismos suportam isso:

  • Gecko 1.9.1 (FireFox 3.5)
  • WebKit (Safari 4, Chrome 3)
  • MSHTML / Trident 6 (IE10) com suporte parcial no IE8 e 9
  • Presto (Opera 12)

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

Resposta Mike Goodwin é ótimo, mas parecia, quando eu tentei, que foi destinado a MVC5 / WebApi 2.1. As dependencies do Microsoft.AspNet.WebApi.Cors não funcionaram muito bem com o meu projeto MVC4.

A maneira mais simples de ativar o CORS no WebApi com o MVC4 foi a seguinte.

Observe que eu permiti tudo, sugiro que você limite a origem apenas aos clientes que deseja que sua API atenda. Permitir tudo é um risco de segurança.

Web.config:

          

BaseApiController.cs:

Fazemos isso para permitir que o verbo http OPTIONS

  public class BaseApiController : ApiController { public HttpResponseMessage Options() { return new HttpResponseMessage { StatusCode = HttpStatusCode.OK }; } } 

Basta adicionar isso ao seu método Application_OnBeginRequest (isso ativará o suporte a CORS globalmente para seu aplicativo) e “manipular” as solicitações de comprovação:

 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(); } 

* segurança: esteja ciente de que isso permitirá que as solicitações do ajax de qualquer lugar para o seu servidor (em vez disso, você só pode permitir uma lista separada por vírgulas de origens / URLs, se preferir).

Eu usei a origem do cliente atual em vez de * porque isso permitirá que as credenciais => configuração de Access-Control-Allow-Credentials para true habilitará gerenciamento de session entre navegadores

você também precisa ativar os verbos delete e put, patch e options na sua seção webconfig system.webServer , caso contrário o IIS irá bloqueá-los:

         

espero que isto ajude

Depois de encontrar o mesmo problema em um projeto da Web API 2 (e não conseguir usar os pacotes CORS padrão por razões que não valem a pena aqui), consegui resolver isso implementando um DelagatingHandler personalizado:

 public class AllowOptionsHandler : DelegatingHandler { protected override async Task 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; } } 

Para a configuração da API da Web:

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

Observe que também tenho os headers CORS habilitados no Web.config, semelhantes a algumas das outras respostas postadas aqui:

                  

Observe que meu projeto não inclui MVC, somente Web API 2.

Consegui superar os erros 405 e 404 lançados em solicitações de opções de pré-ajax apenas por código personalizado em 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: Considere problemas de segurança ao permitir tudo *.

Eu tive que desativar o CORS desde que ele estava retornando ‘Access-Control-Allow-Origin’ header contém vários valores.

Também precisava disso no web.config:

       

E app.pool precisa ser definido para o modo integrado.

Eu tenho esse mesmo problema. Para mim, a correção foi remover o tipo de conteúdo personalizado da chamada do jQuery AJAX. Tipos de conteúdo personalizados acionam a solicitação de pré-voo. Eu achei isto:

O navegador pode ignorar a solicitação de comprovação se as seguintes condições forem verdadeiras:

O método de solicitação é GET , HEAD ou POST e

O aplicativo não define nenhum header de solicitação diferente de Accept , Accept-Language , Content-Language , Content-Type ou Last-Event-ID ;

O header Content-Type (se definido) é um dos seguintes:

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

A partir desta página: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api (em “Solicitações de comprovação”)

No ASP.NET web api 2, o suporte ao CORS foi adicionado. Por favor, verifique o link [ http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api ]

  protected void Application_EndRequest() { if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" ) { Response.Clear(); Response.StatusCode = 200; Response.End(); } } 

Eu também enfrentei o mesmo problema.

Siga a etapa abaixo para resolver o problema na conformidade (CORS) nos navegadores.

Inclua REDRock em sua solução com a referência Cors. Inclua a referência WebActivatorEx à solução de API da Web.

Em seguida, adicione o arquivo CorsConfig na pasta App_Start da API da Web.

 [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()); } } } 

Com essas alterações feitas, consegui acessar o webapi em todos os navegadores.

Eu tive o mesmo problema, e é assim que eu consertei:

Apenas jogue isso no seu web.config: