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);
Kjensen
fonte
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

jgauffin
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

LaundroMatt
fonte
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);
    }
}
Ken Smith
fonte
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 :)

Zvonimir Tokic
fonte
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) 
        {
Bishoy Hanna
fonte
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", "*"); 
Gopichandar
fonte
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: "*")]

GrandMasterFlush
fonte
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);
    }
Pranav Labhe
fonte
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 !!

Trilok Pathak
fonte
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));
}
pkmelee337
fonte
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>
Elvis Skensberg
fonte
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>
olsh
fonte
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!

Ben Power
fonte