Configuración de Access-Control-Allow-Origin en ASP.Net MVC: el método más simple posible

206

Tengo un método de acción simple, que devuelve algo de json. Se ejecuta en ajax.example.com. Necesito acceder a esto desde otro sitio someothersite.com.

Si trato de llamarlo, obtengo el esperado ...:

Origin http://someothersite.com is not allowed by Access-Control-Allow-Origin.

Sé de dos maneras de evitar esto: JSONP y crear un HttpHandler personalizado para establecer el encabezado.

¿No hay una manera más simple?

¿No es posible para una acción simple definir una lista de orígenes permitidos o simplemente permitir a todos? Tal vez un filtro de acción?

Lo óptimo sería ...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);
Kjensen
fuente
1
Por favor, eche un vistazo aquí para vNext y MVC6: neelbhatt40.wordpress.com/2015/09/10/…
Neel

Respuestas:

382

Para controladores simples ASP.NET MVC

Crea un nuevo atributo

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
        base.OnActionExecuting(filterContext);
    }
}

Etiqueta tu acción:

[AllowCrossSiteJson]
public ActionResult YourMethod()
{
    return Json("Works better?");
}

Para la API web ASP.NET

using System;
using System.Web.Http.Filters;

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext.Response != null)
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");

        base.OnActionExecuted(actionExecutedContext);
    }
}

Etiquetar un controlador API completo:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

O llamadas API individuales:

[AllowCrossSiteJson]
public IEnumerable<PartViewModel> Get()
{
    ...
}

Para Internet Explorer <= v9

IE <= 9 no es compatible con CORS. He escrito un javascript que enrutará automáticamente esas solicitudes a través de un proxy. Todo es 100% transparente (solo tiene que incluir mi proxy y el script).

Descárguelo usando nuget corsproxyy siga las instrucciones incluidas.

Publicación de blog | Código fuente

jgauffin
fuente
8
¡Asombroso! Me encanta MVC + U!
Piotr Kula
2
asombrado por la elegancia de esta solución
BraveNewMath
3
Puede extender fácilmente el atributo para aceptar un origen específico si desea limitar CORS a sus propios dominios.
Petrus Theron
2
Debería poder agregar esto a RegisterHttpFilters en su App_Start \ FilterConfig ¿correcto? Hacerlo lo aplicaría a todos los controladores Api en su proyecto. Acoplando esto con el comentario de paté anterior, podría limitar CORS a sus dominios para todos los controladores.
bdwakefield
9
Recientemente actualicé nuestro proyecto a MVC 5 e intenté hacer esto. Incluso agregar el encabezado en un filtro no parece funcionar. Cuando veo la solicitud en la red, el encabezado no está presente en la respuesta. ¿Hay algo más que deba hacerse para que esto funcione?
Kneemin
121

Si está utilizando IIS 7+, puede colocar un archivo web.config en la raíz de la carpeta con esto en la sección system.webServer:

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

Ver: http://msdn.microsoft.com/en-us/library/ms178685.aspx y: http://enable-cors.org/#how-iis7

LaundroMatt
fuente
1
Ya no recuerdo por qué, pero este método no siempre funciona en IIS 7+
LaundroMatt
Hmm La única razón por la que puedo pensar que no funcionaría es si una solicitud se origina en un navegador que no es CORS. Pero continuaré investigando.
sellmeadog
29
Además, esto haría que todo el sitio web fuera compatible con CORS. Si alguien quiere marcar solo una acción o controlador como compatible con CORS, entonces la respuesta aceptada es mucho mejor.
Lev Dubinets
1
Si ve la sección ASP.Net , tiene una pista : "Nota: este enfoque es compatible con IIS6, IIS7 Classic Mode y IIS7 Integrated Mode".
percebus
1
Me enfrento a un problema entre dominios cuando publico mi aplicación en el entorno de SharePoint. Cuando ejecuto mi aplicación en un entorno local, mi aplicación funciona bien, pero cuando la publico en azul en mi sitio sharepoint redirige a la página de error en Ajax. Intenté esta solución pero no me funciona. ¿Hay alguna otra alternativa?
Jyotsna Wadhwani
22

Me encontré con un problema en el que el navegador se negaba a entregar contenido que había recuperado cuando la solicitud pasó en cookies (por ejemplo, el xhr tenía su withCredentials=true), y el sitio se había Access-Control-Allow-Originconfigurado *. (El error en Chrome fue: "No se puede usar comodín en Access-Control-Allow-Origin cuando el indicador de credenciales es verdadero").

Sobre la base de la respuesta de @jgauffin, creé esto, que es básicamente una forma de evitar esa verificación de seguridad del navegador en particular, por lo que se debe advertir.

public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // We'd normally just use "*" for the allow-origin header, 
        // but Chrome (and perhaps others) won't allow you to use authentication if
        // the header is set to "*".
        // TODO: Check elsewhere to see if the origin is actually on the list of trusted domains.
        var ctx = filterContext.RequestContext.HttpContext;
        var origin = ctx.Request.Headers["Origin"];
        var allowOrigin = !string.IsNullOrWhiteSpace(origin) ? origin : "*";
        ctx.Response.AddHeader("Access-Control-Allow-Origin", allowOrigin);
        ctx.Response.AddHeader("Access-Control-Allow-Headers", "*");
        ctx.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        base.OnActionExecuting(filterContext);
    }
}
Ken Smith
fuente
Esto fue especialmente útil, gracias.
cklimowski
15

Esto es realmente simple, solo agregue esto en web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="http://localhost" />
      <add name="Access-Control-Allow-Headers" value="X-AspNet-Version,X-Powered-By,Date,Server,Accept,Accept-Encoding,Accept-Language,Cache-Control,Connection,Content-Length,Content-Type,Host,Origin,Pragma,Referer,User-Agent" />
      <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, OPTIONS" />
      <add name="Access-Control-Max-Age" value="1000" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

En Origin, coloque todos los dominios que tienen acceso a su servidor web, en los encabezados coloque todos los encabezados posibles que cualquier solicitud ajax http puede usar, en los métodos coloque todos los métodos que permita en su servidor

Saludos :)

Zvonimir Tokic
fuente
Agregar "Autorización" en Access-Control-Allow-Headers también puede ser útil si tiene la intención de utilizar consultas autorizadas.
AFract
9

A veces, el verbo OPTIONS también causa problemas

Simplemente: actualice su web.config con lo siguiente

<system.webServer>
    <httpProtocol>
        <customHeaders>
          <add name="Access-Control-Allow-Origin" value="*" />
          <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Y actualice los encabezados del servicio web / controlador con httpGet y httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {
Bishoy Hanna
fuente
Por cierto, en sitefinity necesita agregar * a la configuración avanzada del sistema en la sección de seguridad
Bishoy Hanna
¿Cuáles son los archivos en los que necesito actualizar los encabezados del controlador?
user3281466
5

Agregue esta línea a su método, si está utilizando una API.

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); 
Gopichandar
fuente
4

Este tutorial es muy útil. Para dar un resumen rápido:

  1. Use el paquete CORS disponible en Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. En su WebApiConfig.csarchivo, agregue config.EnableCors()al Register()método.

  3. Agregue un atributo a los controladores que necesita para manejar cors:

[EnableCors(origins: "<origin address in here>", headers: "*", methods: "*")]

GrandMasterFlush
fuente
Tuve que usar este método porque necesitaba establecer un encabezado personalizado en mi solicitud, y el método de atributo personalizado no funcionaba con la solicitud previa al vuelo del navegador. Esto parece funcionar en todos los casos.
lehn0058
3
    public ActionResult ActionName(string ReqParam1, string ReqParam2, string ReqParam3, string ReqParam4)
    {
        this.ControllerContext.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin","*");
         /*
                --Your code goes here --
         */
        return Json(new { ReturnData= "Data to be returned", Success=true }, JsonRequestBehavior.AllowGet);
    }
Pranav Labhe
fuente
2

Hay diferentes formas en que podemos pasar los encabezados de control de acceso-exposición.

  • Como ha explicado jgauffin, podemos crear un nuevo atributo.
  • Como ha explicado LaundroMatt, podemos agregar el archivo web.config.
  • Otra forma es agregar código como se muestra a continuación en el archivo webApiconfig.cs.

    config.EnableCors (nuevo EnableCorsAttribute (" ", encabezados: " ", métodos: "*", expuestosHeaders: "TestHeaderToExpose") {SupportsCredentials = true});

O podemos agregar el siguiente código en el archivo Global.Asax.

protected void Application_BeginRequest()
        {
            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, POST, OPTIONS");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:4200");
                HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers", "TestHeaderToExpose");
                HttpContext.Current.Response.End();
            }
        }

Lo he escrito para las opciones. Modifique lo mismo según su necesidad.

Feliz codificación !!

Trilok Pathak
fuente
1

Después de luchar durante toda una noche, finalmente conseguí que esto funcionara. Después de algunas depuraciones, encontré que el problema con el que me encontraba era que mi cliente estaba enviando una llamada solicitud de Opciones de verificación previa para verificar si la aplicación podía enviar una solicitud de publicación con el origen, los métodos y los encabezados provistos. No quería usar Owin o un APIController, así que comencé a cavar y se me ocurrió la siguiente solución con solo un ActionFilterAttribute. Especialmente la parte "Acceso-Control-Permitir-Encabezados" es muy importante, ya que los encabezados mencionados allí tienen que coincidir con los encabezados que enviará su solicitud.

using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyNamespace
{
    public class AllowCrossSiteJsonAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpRequest request = HttpContext.Current.Request;
            HttpResponse response = HttpContext.Current.Response;

            // check for preflight request
            if (request.Headers.AllKeys.Contains("Origin") && request.HttpMethod == "OPTIONS")
            {
                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                response.End();
            }
            else
            {
                HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                HttpContext.Current.Response.Cache.SetNoStore();

                response.AppendHeader("Access-Control-Allow-Origin", "*");
                response.AppendHeader("Access-Control-Allow-Credentials", "true");
                if (request.HttpMethod == "POST")
                {
                    response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
                    response.AppendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-RequestDigest, Cache-Control, Content-Type, Accept, Access-Control-Allow-Origin, Session, odata-version");
                }

                base.OnActionExecuting(filterContext);
            }
        }
    }
}

Finalmente, mi método de acción MVC se ve así. Importante aquí es mencionar también las Opciones HttpVerbs, porque de lo contrario la solicitud de verificación previa fallará.

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Options)]
[AllowCrossSiteJson]
public async Task<ActionResult> Create(MyModel model)
{
    return Json(await DoSomething(model));
}
pkmelee337
fuente
0

En Web.config ingrese lo siguiente

<system.webServer>
<httpProtocol>
  <customHeaders>
    <clear />     
    <add name="Access-Control-Allow-Credentials" value="true" />
    <add name="Access-Control-Allow-Origin" value="http://localhost:123456(etc)" />
  </customHeaders>
</httpProtocol>
Elvis Skensberg
fuente
0

Si usa IIS, le sugiero que pruebe el módulo IIS CORS .
Es fácil de configurar y funciona para todo tipo de controladores.

Aquí hay un ejemplo de configuración:

    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="*" />
            <add origin="https://*.microsoft.com"
                 allowCredentials="true"
                 maxAge="120"> 
                <allowHeaders allowAllRequestedHeaders="true">
                    <add header="header1" />
                    <add header="header2" />
                </allowHeaders>
                <allowMethods>
                     <add method="DELETE" />
                </allowMethods>
                <exposeHeaders>
                    <add header="header1" />
                    <add header="header2" />
                </exposeHeaders>
            </add>
            <add origin="http://*" allowed="false" />
        </cors>
    </system.webServer>
olsh
fuente
0

Estoy usando DotNet Core MVC y después de luchar durante unas horas con paquetes nuget, Startup.cs, atributos y este lugar, simplemente agregué esto a la acción MVC:

Response.Headers.Add("Access-Control-Allow-Origin", "*");

Me doy cuenta de que esto es bastante torpe, pero es todo lo que necesitaba, y nada más quería agregar esos encabezados. ¡Espero que esto ayude a alguien más!

Ben Power
fuente