Настройка Access-Control-Allow-Origin в ASP.Net MVC - простейший возможный метод

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

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

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

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

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

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

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

return json(mydata, JsonBehaviour.IDontCareWhoAccessesMe);

Ответ 1

Для простых 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?");
}

Для веб-API 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);
    }
}

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

[AllowCrossSiteJson]
public class ValuesController : ApiController
{

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

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

Для Internet Explorer <= v9

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

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

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

Ответ 2

Если вы используете 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

Ответ 3

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

Основываясь на ответе от @jgauffin, я создал это, что в основном является способом обойти эту конкретную проверку безопасности браузера, поэтому caveat 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);
    }
}

Ответ 4

Это действительно просто, просто добавьте это в 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 помещаются все домены, которые имеют доступ к вашему веб-серверу, в заголовках помещаются все возможные заголовки, которые может использовать любой запрос ajax http, в методах ставят все методы, которые вы разрешаете на своем сервере

приветствует:)

Ответ 5

Иногда аргумент 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>

И обновите заголовки webservice/controller с помощью httpGet и httpOptions

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

Ответ 7

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

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

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

  • Добавьте атрибут к контроллерам, которым необходимо обработать cors:

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

Мы можем передать заголовки 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();
            }
        }

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

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

Ответ 11

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

Ответ 12

Если вы используете 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>

Ответ 13

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