Настройка Access-Control-Allow-Origin в ASP.Net MVC - самый простой способ

206

У меня есть простой метод action, который возвращает JSON. Он работает на ajax.example.com. Мне нужно получить доступ к этому с другого сайта someothersite.com.

Если я попытаюсь назвать это, я получу ожидаемое ...:

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

Я знаю два способа обойти это: JSONP и создание собственного HttpHandler для установки заголовка.

Нет ли более простого способа?

Разве невозможно для простого действия определить список разрешенных источников или просто разрешить всем? Может быть, фильтр действий?

Оптимальным было бы ...

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);
Kjensen
источник
1
Пожалуйста, посмотрите здесь vNext и MVC6: neelbhatt40.wordpress.com/2015/09/10/…
Neel

Ответы:

382

Для простых контроллеров ASP.NET MVC

Создать новый атрибут

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

Отметьте свое действие:

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

Для ASP.NET Web API

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

Пометить весь контроллер API:

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

Или отдельные вызовы API:

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

Для Internet Explorer <= v9

IE <= 9 не поддерживает CORS. Я написал javascript, который будет автоматически направлять эти запросы через прокси. Это все на 100% прозрачно (вам просто нужно включить мой прокси и скрипт).

Загрузите его, используя nuget, corsproxyи следуйте прилагаемым инструкциям.

Сообщение в блоге | Исходный код

jgauffin
источник
8
Удивительный! Я люблю MVC + U!
Петр Кула
2
в восторге от элегантности этого решения
BraveNewMath
3
Вы можете легко расширить атрибут для принятия определенного источника, если хотите ограничить CORS для своих собственных доменов.
Петрус Терон
2
Вы должны быть в состоянии добавить это в RegisterHttpFilters в вашем App_Start \ FilterConfig правильно? Это применимо ко всем контроллерам Api в вашем проекте. Сопоставляя это с комментарием Пейта выше, вы можете ограничить CORS вашим доменом (ами) для всех контроллеров.
Bdwakefield
9
Я недавно обновил наш проект до MVC 5 и попытался это сделать. Кажется, даже добавление заголовка в фильтр не работает. Когда я просматриваю запрос в сети, заголовок отсутствует в ответе. Что-нибудь еще нужно сделать, чтобы заставить это работать?
Kneemin
121

Если вы используете IIS 7+, вы можете поместить файл web.config в корень папки с этим в разделе system.webServer:

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

См .: http://msdn.microsoft.com/en-us/library/ms178685.aspx И: http://enable-cors.org/#how-iis7

LaundroMatt
источник
1
Я больше не могу вспомнить почему, но этот метод не всегда работает в IIS 7+
LaundroMatt
Хм. Единственная причина, по которой я могу думать, что это не сработает, это если запрос исходит из браузера не-CORS. Но я продолжу расследовать.
sellmeadog
29
Кроме того, это сделало бы весь сайт CORS-friendly. Если кто-то хочет пометить только одно действие или контроллер как CORS-friendly, то принятый ответ будет намного лучше.
Лев Дубинец
1
Если вы видите раздел ASP.Net , у него есть подсказка : «Примечание: этот подход совместим с IIS6, IIS7 Classic Mode и IIS7 Integrated Mode».
Percebus
1
Я сталкиваюсь с междоменной проблемой, когда публикую свое приложение в среде SharePoint. Когда я запускаю свое приложение в локальной среде, мое приложение работает нормально, но когда я публикую его в Azure на свой сайт sharepoint, оно перенаправляет на страницу ошибок при вызове формы Ajax.Begin. Я попробовал это решение, но оно не работает для меня. Есть ли другая альтернатива этому?
Jyotsna Wadhwani
22

Я столкнулся с проблемой, когда браузер отказывался обслуживать контент, который он извлек, когда запрос передавался в файлах cookie (например, xhr имел его withCredentials=true), и сайт Access-Control-Allow-Originнастроен на *. (Ошибка в Chrome: «Невозможно использовать подстановочный знак в Access-Control-Allow-Origin, когда флаг учетных данных имеет значение true».)

Основываясь на ответе @jgauffin, я создал его, который, по сути, является способом обхода конкретной проверки безопасности браузера, поэтому будьте осторожны с 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);
    }
}
Кен Смит
источник
Это было особенно полезно, спасибо.
cklimowski
15

Это действительно просто, просто добавьте это в 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>

В Origin поместите все домены, которые имеют доступ к вашему веб-серверу, в заголовки поместите все возможные заголовки, которые может использовать любой HTTP-запрос ajax, в методы поместите все методы, которые вы разрешаете на своем сервере.

С уважением :)

Звонимир Токич
источник
Добавление «Авторизация» в Access-Control-Allow-Headers также может быть полезно, если вы собираетесь использовать авторизованные запросы.
ПОЗДРАВЛЯЙ
9

Иногда глагол OPTIONS также вызывает проблемы

Просто: обновите ваш web.config следующим

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

И обновите заголовки веб-службы / контроллера с помощью httpGet и httpOptions

// GET api/Master/Sync/?version=12121
        [HttpGet][HttpOptions]
        public dynamic Sync(string version) 
        {
Бишой ханна
источник
Кстати, в sitefinity вам нужно добавить * к расширенным настройкам системы в разделе «Безопасность»
Bishoy Hanna
Какие файлы, в которых мне нужно обновить заголовки контроллера?
user3281466
5

Добавьте эту строку в ваш метод, если вы используете API.

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); 
Gopichandar
источник
4

Этот урок очень полезен. Чтобы дать краткое резюме:

  1. Используйте пакет CORS, доступный на Nuget: Install-Package Microsoft.AspNet.WebApi.Cors

  2. В вашем WebApiConfig.csфайле добавьте config.EnableCors()в Register()метод.

  3. Добавьте атрибут к контроллерам, которые вам нужны для обработки cors:

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

GrandMasterFlush
источник
Я должен был использовать этот метод, потому что мне нужно было установить пользовательский заголовок в моем запросе, а метод пользовательского атрибута не работал с предварительным запросом браузера. Кажется, это работает во всех случаях.
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

Мы можем передать заголовки Access-Control-Expose-Headers разными способами.

  • Как объяснил jgauffin, мы можем создать новый атрибут.
  • Как пояснил LaundroMatt, мы можем добавить файл web.config.
  • Другой способ - мы можем добавить код, как показано ниже в файле webApiconfig.cs.

    config.EnableCors (new EnableCorsAttribute (" ", headers: " ", методы: "*" ,posedHeaders: "TestHeaderToExpose") {SupportsCredentials = true});

Или мы можем добавить приведенный ниже код в файл 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();
            }
        }

Я написал это для вариантов. Пожалуйста, измените так же, как вам нужно.

Удачного кодирования!

Трилок Патхак
источник
1

После целого вечера борьбы я наконец получил это на работу. После некоторой отладки я обнаружил, что проблема, с которой я сталкивался, заключалась в том, что мой клиент отправлял так называемый запрос параметров предварительной проверки, чтобы проверить, разрешено ли приложению отправлять запрос по почте с указанием источника, методов и заголовков. Я не хотел использовать Owin или APIController, поэтому я начал копать и нашел следующее решение с помощью только ActionFilterAttribute. Особенно важна часть «Access-Control-Allow-Headers», поскольку упомянутые там заголовки должны совпадать с заголовками, которые отправит ваш запрос.

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

Наконец, мой метод действия MVC выглядит следующим образом. Здесь важно также упомянуть параметры HttpVerbs, потому что в противном случае предварительный запрос не будет выполнен.

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Options)]
[AllowCrossSiteJson]
public async Task<ActionResult> Create(MyModel model)
{
    return Json(await DoSomething(model));
}
pkmelee337
источник
0

В Web.config введите следующее

<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

Если вы используете IIS, я бы предложил попробовать модуль IIS CORS .
Он прост в настройке и работает для всех типов контроллеров.

Вот пример конфигурации:

    <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
источник
0

Я использую DotNet Core MVC и после нескольких часов борьбы с пакетами nuget, Startup.cs, атрибутами и этим местом я просто добавил это к действию MVC:

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

Я понимаю, что это довольно неуклюже, но это все, что мне было нужно, и больше ничего не хотелось добавлять эти заголовки. Я надеюсь, что это помогает кому-то еще!

Бен Пауэр
источник