Asp.Net WebApi2 Habilita CORS que no funciona con AspNet.WebAPI.Cors 5.2.3


Traté de seguir los pasos en http://enable-cors.org/server_aspnet.html para tener mi API RESTful (implementada con ASP.NET WebApi2) funciona con solicitudes de origen cruzado (CORS habilitado). No funciona a menos que modifique la web.config.

He instalado WebAPI Cors dependencia:

install-package Microsoft.AspNet.WebApi.Cors -ProjectName MyProject.Web.Api

Entonces en mi App_Start tengo la clase WebApiConfig de la siguiente manera:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var corsAttr = new EnableCorsAttribute("*", "*", "*");
        config.EnableCors(corsAttr);

        var constraintsResolver = new DefaultInlineConstraintResolver();

        constraintsResolver.ConstraintMap.Add("apiVersionConstraint", typeof(ApiVersionConstraint));
        config.MapHttpAttributeRoutes(constraintsResolver); 
        config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));
        //config.EnableSystemDiagnosticsTracing(); 
        config.Services.Replace(typeof(ITraceWriter), new SimpleTraceWriter(WebContainerManager.Get<ILogManager>())); 
        config.Services.Add(typeof(IExceptionLogger), new SimpleExceptionLogger(WebContainerManager.Get<ILogManager>()));
        config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler()); 
    }
}

Pero después de eso corro la aplicación, solicito un recurso con Fiddler como: http://localhost:51589/api/v1/persons y en la respuesta no puedo ver las cabeceras HTTP que debería ver como:

  • Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
  • Access-Control-Allow-Origin: *

¿Me estoy perdiendo algún paso? He intentado con la siguiente anotación en el controlador:

[EnableCors(origins: "http://example.com", headers: "*", methods: "*")]

El mismo resultado, no hay CORS habilitado.

Sin embargo, si añado lo siguiente en mi web.config (sin siquiera instalar la AspNet.WebAPI.Dependencia de Cors) obras:

<system.webServer>

<httpProtocol>
  <!-- THESE HEADERS ARE IMPORTANT TO WORK WITH CORS -->
  <!--
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Methods" value="POST, PUT, DELETE, GET, OPTIONS" />
    <add name="Access-Control-Allow-Headers" value="content-Type, accept, origin, X-Requested-With, Authorization, name" />
    <add name="Access-Control-Allow-Credentials" value="true" />
  </customHeaders>
  -->
</httpProtocol>
<handlers>
  <!-- THESE HANDLERS ARE IMPORTANT FOR WEB API TO WORK WITH  GET,HEAD,POST,PUT,DELETE and CORS-->
  <!--

  <remove name="WebDAV" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,PUT,DELETE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  <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>

Cualquier ayuda sería muy apreciada!

Gracias.

Author: Shaun Luttin, 2015-03-13

10 answers

He creado un proyecto de demostración reducido para ti.

Puede probar el enlace anterior a la API desde su Violinista local para ver los encabezados. Aquí hay una explicación.

Global.ascx

Todo lo que esto hace es llamar al WebApiConfig. No es más que código. organismo.

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        WebApiConfig.Register(GlobalConfiguration.Configuration);
    }
}

WebApiConfig.cs

El método clave para su aquí es el método EnableCrossSiteRequests. Esto es todo que necesitas hacer. El EnableCorsAttribute es un atributo CORS de alcance global.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        EnableCrossSiteRequests(config);
        AddRoutes(config);
    }

    private static void AddRoutes(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Default",
            routeTemplate: "api/{controller}/"
        );
    }

    private static void EnableCrossSiteRequests(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute(
            origins: "*", 
            headers: "*", 
            methods: "*");
        config.EnableCors(cors);
    }
}

Controlador de valores

El método Get recibe el atributo EnableCors que aplicamos globalmente. El método Another reemplaza al EnableCors global.

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { 
            "This is a CORS response.", 
            "It works from any origin." 
        };
    }

    // GET api/values/another
    [HttpGet]
    [EnableCors(origins:"http://www.bigfont.ca", headers:"*", methods: "*")]
    public IEnumerable<string> Another()
    {
        return new string[] { 
            "This is a CORS response. ", 
            "It works only from two origins: ",
            "1. www.bigfont.ca ",
            "2. the same origin." 
        };
    }
}

Web.config

No es necesario agregar cualquier cosa especial en la web.config. De hecho, esto es lo que la demo de la web.config parece-está vacío.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>

Demo

var url = "https://cors-webapi.azurewebsites.net/api/values"

$.get(url, function(data) {
  console.log("We expect this to succeed.");
  console.log(data);
});

var url = "https://cors-webapi.azurewebsites.net/api/values/another"

$.get(url, function(data) {
  console.log(data);
}).fail(function(xhr, status, text) {
  console.log("We expect this to fail.");
  console.log(status);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
 80
Author: Shaun Luttin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-24 14:22:27

Solo necesita cambiar algunos archivos. Esto funciona para mí.

Global.ascx

public class WebApiApplication : System.Web.HttpApplication {
    protected void Application_Start()
    {
        WebApiConfig.Register(GlobalConfiguration.Configuration);
    } }

WebApiConfig.cs

Todas las solicitudes tienen que llamar a este código.

public static class WebApiConfig {
    public static void Register(HttpConfiguration config)
    {
        EnableCrossSiteRequests(config);
        AddRoutes(config);
    }

    private static void AddRoutes(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Default",
            routeTemplate: "api/{controller}/"
        );
    }

    private static void EnableCrossSiteRequests(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute(
            origins: "*", 
            headers: "*", 
            methods: "*");
        config.EnableCors(cors);
    } }

Algún Controlador

Nada que cambiar.

Web.config

Necesitas agregar manejadores en tu web.config

<configuration> 
  <system.webServer>
    <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>   
  </system.webServer> 
</configuration>
 14
Author: Jonas Ribeiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 11:26:37

En caso de solicitud CORS, todos los navegadores modernos responden con un verbo DE OPCIÓN, y luego la solicitud real sigue. Se supone que esto se usa para solicitar al usuario confirmación en caso de una solicitud CORS. Pero en el caso de una API, si desea omitir este proceso de verificación, agregue el siguiente fragmento a Global.asax

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
                HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
                HttpContext.Current.Response.End();
            }
        }

Aquí estamos simplemente pasando la comprobación comprobando el verbo OPTIONS.

 10
Author: Neeraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-30 02:27:22

Acabo de agregar encabezados personalizados a la Web.config y funcionó como un encanto.

En configuración - sistema.servidor:

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type" />
  </customHeaders>
</httpProtocol>

Tengo la aplicación front end y el backend en la misma solución. Para que esto funcione, necesito establecer el proyecto de servicios web (Backend) como el predeterminado para que esto funcione.

Estaba usando ReST, no he intentado con nada más.

 8
Author: Mathter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 18:18:28

Después de algunas modificaciones en mi Web.los CORS de configuración dejaron de funcionar repentinamente en mi proyecto Web API 2 (al menos para la solicitud de OPCIONES durante la comprobación previa). Parece que necesita tener la sección mencionada a continuación en su Web.config o de lo contrario, EnableCorsAttribute (global) no funcionará en las solicitudes de OPCIONES. Tenga en cuenta que esta es exactamente la misma sección que Visual Studio agregará en un nuevo proyecto de Web API 2.

<system.webServer>
  <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>
</system.webServer>
 3
Author: Christian Gürtler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-14 19:23:25

Ninguna de estas respuestas realmente funciona. Como otros señalaron, el paquete Cors solo usará el encabezado Access-Control-Allow-Origin si la solicitud tenía un encabezado Origin. Pero generalmente no puede simplemente agregar un encabezado de Origen a la solicitud porque los navegadores pueden tratar de regular eso también.

Si desea una forma rápida y sucia de permitir solicitudes de sitios cruzados a una api web, es realmente mucho más fácil escribir un atributo de filtro personalizado:

public class AllowCors : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext == null)
        {
            throw new ArgumentNullException("actionExecutedContext");
        }
        else
        {
            actionExecutedContext.Response.Headers.Remove("Access-Control-Allow-Origin");
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
        }
        base.OnActionExecuted(actionExecutedContext);
    }
}

Entonces solo úsalo en tu Controlador medidas:

[AllowCors]
public IHttpActionResult Get()
{
    return Ok("value");
}

No respondo por la seguridad de esto en general, pero probablemente es mucho más seguro que configurar los encabezados en la web.config ya que de esta manera puedes aplicarlos tan específicamente como los necesites.

Y, por supuesto, es simple modificar lo anterior para permitir solo ciertos orígenes, métodos, etc.

 2
Author: Matthew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-16 00:22:48

WEBAPI2: SOLUCIÓN. global.asax.cs:

var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);

EN el explorador de soluciones, haga clic con el botón derecho en api-project. En ventana propiedades establecer 'Autenticación anónima' a Habilitado !!!

Espero que esto ayude a alguien en el futuro.

 2
Author: hannes neukermans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 14:15:54

Acabo de experimentar este mismo problema, tratando de habilitar CORS globalmente. Sin embargo, descubrí que funciona, sin embargo, solo cuando la solicitud contiene un valor de encabezado Origin. Si omite el valor de encabezado origin, la respuesta no contendrá un Access-Control-Allow-Origin.

Usé un complemento de Chrome llamado DHC para probar mi solicitud GET. Me permitió agregar el encabezado Origin fácilmente.

 1
Author: Rick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-16 13:02:35

Encontré esta pregunta porque tenía problemas con las OPCIONES de solicitud que la mayoría de los navegadores envían. Mi aplicación estaba enrutando las solicitudes de OPCIONES y usando mi IoC para construir muchos objetos y algunos estaban lanzando excepciones en este tipo de solicitud extraña por varias razones.

Básicamente ponga una ruta de ignorar para todas las solicitudes de OPCIONES si le están causando problemas:

var constraints = new { httpMethod = new HttpMethodConstraint(HttpMethod.Options) };
config.Routes.IgnoreRoute("OPTIONS", "{*pathInfo}", constraints);

Más información: Detener las solicitudes de OPCIONES de procesamiento de la API web

 0
Author: BritishDeveloper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-22 17:12:00

Espero que esto ayude a alguien en el futuro. Mi problema era que estaba siguiendo el mismo tutorial que el OP para habilitar CORS globales. Sin embargo, también establezco una regla CORS específica de Acción en mi AccountController.archivo cs:

[EnableCors(origins: "", headers: "*", methods: "*")]

Y estaba recibiendo errores sobre el origen no puede ser null o cadena vacía. PERO el error estaba ocurriendo en el Mundo.asax.archivo cs de todos los lugares. La solución es cambiarlo a:

[EnableCors(origins: "*", headers: "*", methods: "*")]

Observe el * en los orígenes? Falta que era lo que estaba causando el error en global.asax.archivo CS.

Espero que esto ayude a alguien.

 0
Author: jangooni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-13 16:09:47