Cómo admitir el verbo HTTP OPTIONS en la aplicación ASP.NET MVC / WebAPI

Configuré una aplicación web ASP.NET comenzando con una plantilla MVC 4 / API web. Parece que las cosas están funcionando realmente bien, sin problemas de los que soy consciente. He usado Chrome y Firefox para recorrer el sitio. He probado usando Fiddler y todas las respuestas parecen estar en el dinero.

Así que ahora procedo a escribir un Test.aspx simple para consumir esta nueva API web. Las partes relevantes del guion:

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

Esto genera un encabezado de SOLICITUD:

 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 es, la API web devuelve un método 405 no 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'. 

Entiendo que el verbo OPTIONS no está conectado en los controladores de la API web de forma predeterminada … Por lo tanto, coloqué el siguiente código en mi UserController.cs:

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

… y esto eliminó el error 405 Método no permitido, pero la respuesta está completamente vacía: no se devuelve ningún dato:

 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 

Debe haber una lógica adicional … No sé cómo codificar correctamente el método de Opciones o si el controlador es incluso el lugar adecuado para poner el código. Es extraño (para mí) que el sitio Web API responda correctamente cuando se lo ve desde Firefox o Chrome, pero la llamada .ajax anterior genera errores. ¿Cómo manejo la verificación de “verificación previa” en el código .ajax? Tal vez debería abordar este problema en la lógica .ajax del cliente? O bien, si esto es un problema en el lado del servidor debido a que no se maneja el verbo OPTIONS.

¿Alguien puede ayudar? Este debe ser un problema muy común y me disculpo si ha sido respondido aquí. Busqué pero no encontré ninguna respuesta que me ayudara.

ACTUALIZAR En mi humilde opinión, este es un problema del lado del cliente y tiene que ver con el código Ajax JQuery anterior. Digo esto porque Fiddler no muestra ningún encabezado de error 405 cuando accedo a mywebapidomain / api / user desde un navegador web. El único lugar donde puedo duplicar este problema es desde la llamada JQuery .ajax (). Además, la llamada anterior de Ajax funciona bien cuando se ejecuta en el servidor (mismo dominio).

Encontré otra publicación: la solicitud del prototipo AJAX se envía como OPCIONES en lugar de GET; resulta en un error 501 que parece estar relacionado, pero he modificado sus sugerencias sin éxito. Aparentemente, JQuery está codificado de modo que si una solicitud de Ajax es de dominio cruzado (que es la mía) agrega un par de encabezados que activan de alguna manera el encabezado de OPCIONES.

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

Parece que debería haber una mejor solución disponible que modificar el código central en JQuery …

La respuesta a continuación supone que se trata de un problema del lado del servidor. Tal vez, supongo, pero me inclino hacia el cliente y llamar a un proveedor de hosting no va a ayudar.

Como dijo Daniel A. White en su comentario, es muy probable que el cliente cree la solicitud de OPTIONS como parte de una solicitud de JavaScript entre dominios. Esto se realiza automáticamente mediante navegadores compatibles con Cross Origin Resource Sharing (CORS). La solicitud es una solicitud preliminar o previa al vuelo , realizada antes de la solicitud real de AJAX para determinar qué verbos de solicitud y cabeceras son compatibles con CORS. El servidor puede elegir admitir ninguno, todos o algunos de los verbos HTTP.

Para completar la imagen, la solicitud de AJAX tiene un encabezado adicional de “Origen”, que identifica dónde se sirvió la página original que aloja el JavaScript. El servidor puede elegir admitir solicitudes desde cualquier origen, o solo para un conjunto de orígenes conocidos y confiables. Permitir cualquier origen es un riesgo de seguridad ya que puede boost el riesgo de falsificación de solicitudes entre sitios (CSRF).

Por lo tanto, debe habilitar CORS.

Aquí hay un enlace que explica cómo hacerlo en ASP.Net Web API

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

La implementación descrita allí le permite especificar, entre otras cosas

  • Soporte de CORS por acción, por controlador o global
  • Los orígenes soportados
  • Al habilitar CORS como controlador o nivel global, los verbos HTTP compatibles
  • Si el servidor admite el envío de credenciales con solicitudes de origen cruzado

En general, esto funciona bien, pero debe asegurarse de estar al tanto de los riesgos de seguridad, especialmente si permite solicitudes cruzadas de origen desde cualquier dominio. Piensa con cuidado antes de permitir esto.

En términos de qué navegadores son compatibles con CORS, Wikipedia dice que los siguientes motores lo admiten:

  • Gecko 1.9.1 (FireFox 3.5)
  • WebKit (Safari 4, Chrome 3)
  • MSHTML / Trident 6 (IE10) con soporte parcial en IE8 y 9
  • Prest (Opera 12)

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

La respuesta de Mike Goodwin es genial, pero cuando probé, parecía que estaba dirigida a MVC5 / WebApi 2.1. Las dependencias de Microsoft.AspNet.WebApi.Cors no funcionaron muy bien con mi proyecto MVC4.

La forma más sencilla de habilitar CORS en WebApi con MVC4 fue la siguiente.

Tenga en cuenta que lo he permitido todo, le sugiero que limite los Origin a solo los clientes a los que desea que sirva su API. Permitir todo es un riesgo de seguridad.

Web.config:

          

BaseApiController.cs:

Hacemos esto para permitir el verbo OPTIONS http

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

Simplemente agregue esto a su método Application_OnBeginRequest (esto habilitará el soporte CORS globalmente para su aplicación) y “maneje” las solicitudes de verificación previa:

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

* seguridad: tenga en cuenta que esto habilitará las solicitudes ajax desde cualquier lugar hasta su servidor (en su lugar, solo puede permitir una lista separada por comas de Origins / urls, si lo prefiere).

Utilicé el origen del cliente actual en lugar de * porque esto permitirá que las credenciales => al establecer Access-Control-Allow-Credentials en true habiliten la gestión de la sesión del navegador cruzado

también debe habilitar los verbos delete y put, patch y options en su sección webconfig system.webServer , de lo contrario, IIS los bloqueará:

         

espero que esto ayude

Después de encontrar el mismo problema en un proyecto Web API 2 (y no poder usar los paquetes CORS estándar por razones que no vale la pena consultar aquí), pude resolver esto implementando un 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 la configuración de la API web:

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

Tenga en cuenta que también tengo los encabezados CORS habilitados en Web.config, similar a algunas de las otras respuestas publicadas aquí:

                  

Tenga en cuenta que mi proyecto no incluye MVC, solo Web API 2.

Logré superar los errores 405 y 404 arrojados en las solicitudes de opciones de ajax previas al vuelo solo por código personalizado en 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(); } } 

PD: Considere los problemas de seguridad al permitir todo *.

Tuve que deshabilitar CORS ya que el encabezado ‘Access-Control-Allow-Origin’ devuelve contiene varios valores.

También se necesita esto en web.config:

       

Y app.pool debe establecerse en modo integrado.

Yo tuve el mísmo problema. Para mí, la solución fue eliminar el tipo de contenido personalizado de la llamada jQuery AJAX. Los tipos de contenido personalizados activan la solicitud previa al vuelo. Encontré esto:

El navegador puede omitir la solicitud de verificación previa si las siguientes condiciones son verdaderas:

El método de solicitud es GET , HEAD o POST , y

La aplicación no establece ningún encabezado de solicitud que no sea Accept , Accept-Language , Content-Language , Content-Type o Last-Event-ID , y

El encabezado Content-Type (si está configurado) es uno de los siguientes:

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

Desde esta página: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api (en “Solicitudes previas al vuelo”)

En ASP.NET web api 2, se ha agregado el soporte CORS. Por favor, consulte el enlace [ 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(); } } 

Yo también enfrenté el mismo problema.

Siga el paso siguiente para resolver el problema del cumplimiento (CORS) en los navegadores.

Incluye REDRock en tu solución con la referencia de Cors. Incluye la referencia de WebActivatorEx a la solución de API web.

A continuación, agregue el archivo CorsConfig en la API Web App_Start Folder.

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

Con estos cambios realizados, pude acceder a los webapi en todos los navegadores.

He tenido el mismo problema, y ​​así es como lo solucioné:

Solo eche esto en su web.config: