Definindo Access-Control-Allow-Origin no ASP.Net MVC - o método mais simples possível


206

Eu tenho um método de ação simples, que retorna algum json. É executado em ajax.example.com. Preciso acessar isso em outro site someothersite.com.

Se eu tentar chamá-lo, recebo o esperado ...:

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

Conheço duas maneiras de contornar isso: JSONP e criar um HttpHandler personalizado para definir o cabeçalho.

Não existe uma maneira mais simples?

Não é possível para uma ação simples definir uma lista de origens permitidas - ou simplesmente permitir a todos? Talvez um filtro de ação?

O ideal seria ...:

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);

1
Dê uma olhada aqui para vNext e MVC6: neelbhatt40.wordpress.com/2015/09/10/…
— Neel

Respostas:


382

Para controladores ASP.NET MVC simples

Crie um novo atributo

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

Etiquete sua ação:

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

Para API da Web do 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);
    }
}

Etiquete um controlador de API inteiro:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

Ou chamadas de API individuais:

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

Para Internet Explorer <= v9

O IE <= 9 não suporta CORS. Eu escrevi um javascript que encaminhará automaticamente essas solicitações por meio de um proxy. É tudo 100% transparente (você só precisa incluir meu proxy e o script).

Faça o download usando pepitas corsproxye siga as instruções incluídas.

Postagem no blog | Código fonte


8
Surpreendente! Eu amo MVC + U!
— Piotr Kula

2
admirado com a elegância desta solução
— BraveNewMath

3
Você poderia estender facilmente o atributo para aceitar uma origem específica, se quisesse limitar o CORS aos seus próprios domínios.
— Petrus Theron

2
Você deve poder adicionar isso aos RegisterHttpFilters no seu App_Start \ FilterConfig correto? Isso o aplicaria a todos os controladores da API no seu projeto. Juntando isso ao comentário do pate acima, você pode limitar o CORS ao (s) seu (s) domínio (s) para todos os controladores.
— bdwakefield

9
Atualizei recentemente nosso projeto para o MVC 5 e tentei fazer isso. Mesmo adicionando o cabeçalho em um filtro não parece funcionar. Quando visualizo a solicitação na rede, o cabeçalho não está presente na resposta. Há mais alguma coisa que precisa ser feita para que isso funcione?
— Kneemin

121

Se você estiver usando o IIS 7+, poderá colocar um arquivo web.config na raiz da pasta, na seção system.webServer:

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

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


1
Não me lembro por quê mais, mas este método nem sempre funciona no IIS 7+
— LaundroMatt

Hmm. A única razão pela qual acho que não funcionaria é se uma solicitação se originar de um navegador que não seja o CORS. Mas vou continuar a investigar.
— sellmeadog

29
Além disso, isso tornaria o site inteiro compatível com o CORS. Se alguém quiser marcar apenas uma única ação ou controlador como compatível com CORS, a resposta aceita será muito melhor.
— precisa saber é o seguinte

1
Se você vir a seção ASP.Net , há uma dica : "Observação: essa abordagem é compatível com o IIS6, o Modo Clássico do IIS7 e o Modo Integrado do IIS7".
— percebus

1
Estou enfrentando um problema entre domínios quando publico meu aplicativo no ambiente do SharePoint. Quando executo meu aplicativo no ambiente local, ele funciona bem, mas quando o publico no azure no site do sharepoint, ele é redirecionado para a página de erro na chamada de formulário Ajax.Begin. Eu tentei esta solução, mas não funciona para mim. Existe alguma outra alternativa para isso?
— Jyotsna Wadhwani

22

Encontrei um problema em que o navegador se recusava a exibir o conteúdo recuperado quando a solicitação passava nos cookies (por exemplo, o xhr tinha o seu withCredentials=true) e o site estava Access-Control-Allow-Origindefinido como *. (O erro no Chrome foi: "Não é possível usar curinga no Access-Control-Allow-Origin quando o sinalizador de credenciais for verdadeiro.")

Com base na resposta de @jgauffin, eu criei isso, que é basicamente uma maneira de contornar essa verificação de segurança do navegador em particular, portanto, faça uma advertência ao emptor.

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

Isso foi especialmente útil, obrigado.
— cklimowski 22/06

15

Isso é realmente simples, basta adicionar isso no 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>

Em Origin, coloque todos os domínios que têm acesso ao servidor da Web, nos cabeçalhos, todos os cabeçalhos possíveis que qualquer solicitação http ajax pode usar; nos métodos, coloque todos os métodos que você permitir no servidor.

Saudações :)


Adicionar "Autorização" a cabeçalhos de controle de acesso e permissão também pode ser útil se você pretende usar consultas autorizadas.
— precisa saber é o seguinte

9

Às vezes, o verbo OPTIONS também causa problemas

Simplesmente: Atualize seu web.config com o seguinte

<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>

E atualize os cabeçalhos de serviço da web / controlador com httpGet e httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {

BTW, em Sitefinity você precisa adicionar * ao sistema avançado configurações na seção de segurança
— Bishoy Hanna

quais são os arquivos nos quais preciso atualizar os cabeçalhos do controlador?
— user3281466


5

Adicione esta linha ao seu método, se você estiver usando uma API.

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

4

Este tutorial é muito útil. Para dar um resumo rápido:

  1. Use o pacote CORS disponível no Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. No seu WebApiConfig.csarquivo, adicione config.EnableCors()ao Register()método

  3. Adicione um atributo aos controladores necessários para lidar com cors:

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


Eu tive que usar esse método porque precisava definir um cabeçalho personalizado na minha solicitação, e o método do atributo personalizado não funcionou com a solicitação de pré-vôo do navegador. Isso parece funcionar em todos os 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);
    }

2

Existem diferentes maneiras de passar os cabeçalhos de controle de acesso e exposição.

  • Como o jgauffin explicou, podemos criar um novo atributo.
  • Como o LaundroMatt explicou, podemos adicionar o arquivo web.config.
  • Outra maneira é adicionar código como abaixo no arquivo webApiconfig.cs.

    config.EnableCors (new EnableCorsAttribute (" ", cabeçalhos: " ", métodos: "*" ,posedHeaders: "TestHeaderToExpose") {SupportsCredentials = true});

Ou podemos adicionar o código abaixo no arquivo 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();
            }
        }

Eu escrevi para as opções. Modifique o mesmo conforme sua necessidade.

Feliz codificação !!


1

Depois de lutar por uma noite inteira, finalmente consegui que isso funcionasse. Após alguma depuração, encontrei o problema em que estava encontrando o problema: meu cliente estava enviando uma solicitação de opções de preflight para verificar se o aplicativo tinha permissão para enviar uma solicitação de postagem com a origem, métodos e cabeçalhos fornecidos. Como não queria usar o Owin ou um APIController, comecei a cavar e criei a seguinte solução com apenas um ActionFilterAttribute. Especialmente a parte "Controle de acesso-Permitir cabeçalhos" é muito importante, pois os cabeçalhos mencionados lá precisam corresponder aos cabeçalhos que sua solicitação enviará.

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, meu método de ação MVC se parece com isso. Importante aqui é também mencionar as Opções HttpVerbs, porque, caso contrário, a solicitação de comprovação falhará.

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

0

Em Web.config, digite o seguinte

<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>

0

Se você usa o IIS, sugiro tentar o módulo IIS CORS .
É fácil de configurar e funciona para todos os tipos de controladores.

Aqui está um exemplo de configuração:

    <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>

0

Estou usando o DotNet Core MVC e depois de lutar por algumas horas com pacotes de nuget, Startup.cs, atributos e este local, simplesmente adicionei isso à ação MVC:

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

Sei que isso é bastante desajeitado, mas é tudo que eu precisava e nada mais queria adicionar esses cabeçalhos. Espero que isto ajude alguém!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.