He configurado una aplicación web ASP.NET comenzando con una plantilla MVC 4 / Web API. Parece que las cosas están funcionando muy bien, no hay problemas que yo sepa. He utilizado Chrome y Firefox para recorrer el sitio. Probé con Fiddler y todas las respuestas parecen estar relacionadas con el dinero.
Así que ahora procedo a escribir un Test.aspx simple para consumir esta nueva API web. Las partes relevantes del guión:
<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>
Esto genera un encabezado 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
Tal como está, Web API 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
<Error><Message>The requested resource does not support http method 'OPTIONS'.</Message></Error>
Entiendo que el verbo OPTIONS no está conectado en los controladores Web API de forma predeterminada ... Entonces, 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 devuelven datos:
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 lógica adicional ... No sé cómo codificar correctamente el método de Opciones o si el controlador es el lugar adecuado para poner el código. Es extraño (para mí) que el sitio de la API web responda correctamente cuando se ve desde Firefox o Chrome, sin embargo, la llamada .ajax menciona los errores anteriores. ¿Cómo manejo la verificación "previa al vuelo" en el código .ajax? ¿Quizás debería abordar este problema en la lógica .ajax del lado del cliente? O, si esto es un problema en el lado del servidor debido a no manejar el verbo OPTIONS.
¿Alguien puede ayudar? Este debe ser un problema muy común y me disculpo si se ha 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 Ajax idéntica anterior funciona bien cuando se ejecuta en el servidor (mismo dominio).
Encontré otra publicación: La solicitud de prototipo AJAX se envía como OPCIONES en lugar de GET; da como resultado un error 501 que parece estar relacionado, pero he modificado sus sugerencias sin éxito. Aparentemente, JQuery está codificado de modo que si una solicitud Ajax es de dominio cruzado (que es la mía), agrega un par de encabezados que activan el encabezado OPTIONS de alguna manera.
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
Simplemente parece que debería haber una mejor solución disponible que modificar el código central en JQuery ...
La respuesta proporcionada a continuación asume que esto es un problema del lado del servidor. Quizás, supongo, pero me inclino por el cliente y llamar a un proveedor de alojamiento no va a ayudar.
fuente
Respuestas:
Como dijo Daniel A. White en su comentario, lo más probable es que el cliente cree la solicitud OPTIONS como parte de una solicitud JavaScript entre dominios. Esto lo hacen automáticamente los navegadores compatibles con Cross Origin Resource Sharing (CORS). La solicitud es una solicitud preliminar o previa al vuelo , realizada antes de la solicitud AJAX real para determinar qué verbos y encabezados de solicitud son compatibles con CORS. El servidor puede optar por admitirlo para ninguno, todos o algunos de los verbos HTTP.
Para completar la imagen, la solicitud AJAX tiene un encabezado "Origen" adicional, que identifica desde dónde se entregó la página original que aloja el JavaScript. El servidor puede optar por admitir solicitudes de cualquier origen, o solo para un conjunto de orígenes conocidos y confiables. Permitir cualquier origen es un riesgo de seguridad, ya que puede aumentar el riesgo de falsificación de solicitudes entre sitios (CSRF).
Por lo tanto, debe habilitar CORS.
Aquí hay un enlace que explica cómo hacer esto 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 que se describe allí le permite especificar, entre otras cosas
En general, esto funciona bien, pero debe asegurarse de conocer los riesgos de seguridad, especialmente si permite solicitudes de origen cruzado desde cualquier dominio. Piense con mucho cuidado antes de permitir esto.
En términos de qué navegadores admiten CORS, Wikipedia dice que los siguientes motores lo admiten:
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing#Browser_support
fuente
La respuesta de Mike Goodwin es excelente, pero parecía, cuando la probé, que estaba dirigida a MVC5 / WebApi 2.1. Las dependencias para Microsoft.AspNet.WebApi.Cors no funcionaron bien con mi proyecto MVC4.
La forma más sencilla de habilitar CORS en WebApi con MVC4 fue la siguiente.
Tenga en cuenta que he permitido todos, le sugiero que limite el Origen solo a los clientes a los que desea que atienda su API. Permitir todo es un riesgo de seguridad.
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:
Hacemos esto para permitir las OPCIONES http verbo
public class BaseApiController : ApiController { public HttpResponseMessage Options() { return new HttpResponseMessage { StatusCode = HttpStatusCode.OK }; } }
fuente
Simplemente agregue esto a su
Application_OnBeginRequest
método (esto habilitará el soporte CORS a nivel mundial para su aplicación) y "manejar" 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 a su servidor (en su lugar, solo puede permitir una lista separada por comas de Orígenes / URL si lo prefiere).
Usé el origen del cliente actual en lugar de
*
porque esto permitirá credenciales => la configuraciónAccess-Control-Allow-Credentials
en verdadero habilitará la administración de sesiones de navegador cruzadotambién debe habilitar los verbos de eliminación y colocación, parche y opciones en su
webconfig
secciónsystem.webServer
, de lo contrario, IIS los bloqueará:<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>
espero que esto ayude
fuente
Application_OnBeginRequest
me ayudó. Pero si también desea poder obtener datos con autorización, también debe agregarAuthorization
aAccess-Control-Allow-Headers
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 mencionar aquí), pude resolverlo implementando un DelagatingHandler personalizado:
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; } }
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í:
<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>
Tenga en cuenta que mi proyecto no incluye MVC, solo Web API 2.
fuente
He logrado superar los errores 405 y 404 lanzados en las solicitudes de opciones de Ajax previas al vuelo solo mediante 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: Tenga en cuenta los problemas de seguridad al permitir todo *.
Tuve que deshabilitar CORS ya que estaba devolviendo el encabezado 'Access-Control-Allow-Origin' que contiene varios valores.
También necesitaba esto en 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>
Y app.pool debe configurarse en modo integrado.
fuente
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:
Desde esta página: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api (en "Solicitudes de verificación previa")
fuente
En ASP.NET web api 2, se ha agregado compatibilidad con CORS. Consulte el enlace [ http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api ]
fuente
protected void Application_EndRequest() { if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" ) { Response.Clear(); Response.StatusCode = 200; Response.End(); } }
fuente
Yo también enfrenté el mismo problema.
Siga el paso a continuación para resolver el problema de conformidad (CORS) en los navegadores.
Incluya REDRock en su solución con la referencia de Cors. Incluya la referencia de WebActivatorEx a la solución Web API.
Luego, agregue el archivo CorsConfig en la carpeta App_Start de la API 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()); } } }
Con estos cambios realizados, pude acceder a webapi en todos los navegadores.
fuente
Tuve el mismo problema, y así es como lo solucioné:
Solo lanza esto en tu 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>
fuente
//In the Application_OnBeginRequest method in GLOBAL.ASX add the following:- var res = HttpContext.Current.Response; var req = HttpContext.Current.Request; res.AppendHeader("Access-Control-Allow-Origin", "*"); res.AppendHeader("Access-Control-Allow-Credentials", "true"); res.AppendHeader("Access-Control-Allow-Headers", "Authorization"); 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(); } //Remove any entries in the custom headers as this will throw an error that there's to //many values in the header. <httpProtocol> <customHeaders> </customHeaders> </httpProtocol>
fuente