AngularJS выполняет HTTP-запрос OPTIONS для кросс-исходного ресурса

Я пытаюсь настроить AngularJS для связи с ресурсом кросс-происхождения, где хост-ресурс, который доставляет мои файлы шаблонов, находится в другом домене, и поэтому запрос XHR, который выполняет угловое выполнение, должен быть междоменным. Я добавил соответствующий заголовок CORS на свой сервер для HTTP-запроса, чтобы выполнить эту работу, но он, похоже, не работает. Проблема в том, что при проверке HTTP-запросов в моем браузере (хром) запрос, отправленный в файл актива, является запросом OPTIONS (это должен быть запрос GET).

Я не уверен, является ли это ошибкой в ​​AngularJS или мне нужно что-то настроить. Из того, что я понимаю, XHR-обёртка не может сделать HTTP-запрос OPTIONS, поэтому похоже, что браузер пытается выяснить, разрешено ли «загрузка» актива прежде, чем он выполнит запрос GET. Если это так, то мне нужно установить заголовок CORS (Access-Control-Allow-Origin: http://asset.host … ) Вместе с хостом-ресурсом?

Запрос OPTIONS ни в коем случае не является ошибкой AngularJS, так как стандарт Cross-Origin Resource Sharing обязывает браузеры вести себя. См. Этот документ: https://developer.mozilla.org/en-US/docs/HTTP_access_control , где в разделе «Обзор» говорится:

Стандарт совместного использования ресурсов Cross-Origin работает, добавляя новые HTTP-заголовки, которые позволяют серверам описывать набор истоков, которым разрешено читать эту информацию с помощью веб-браузера. Кроме того, для методов HTTP-запросов, которые могут вызывать побочные эффекты для пользовательских данных (в частности, для HTTP-методов, отличных от GET, или для использования POST с определенными типами MIME). Спецификация предусматривает, что браузеры «предваряют» запрос, запрашивая поддерживаемые методы с сервера с заголовком запроса HTTP OPTIONS, а затем после «утверждения» с сервера отправляют фактический запрос с помощью фактического метода HTTP-запроса. Серверы также могут уведомлять клиентов о том, должны ли отправляться с запросами «учетные данные» (включая cookies и данные HTTP-аутентификации).

Очень сложно предоставить общее решение, которое будет работать на всех WWW-серверах, поскольку настройка будет зависеть от самого сервера и HTTP-глаголов, которые вы собираетесь поддерживать. Я бы посоветовал вам ознакомиться с этой замечательной статьей ( http://www.html5rocks.com/en/tutorials/cors/ ), в которой содержится более подробная информация о точках заголовков, которые должны быть отправлены сервером.

Для Angular 1.2.0rc1 + вам нужно добавить resourceUrlWhitelist.

1.2: выпуск версии, они добавили функцию escapeForRegexp, чтобы вам больше не приходилось скрывать строки. Вы можете просто добавить URL напрямую

 'http://sub*.assets.example.com/**' 

обязательно добавьте ** для вспомогательных папок. Вот рабочий jsbin для 1.2: http://jsbin.com/olavok/145/edit


1.2.0rc: Если вы все еще используете версию rc, решение Angular 1.2.0rc1 выглядит так:

 .config(['$sceDelegateProvider', function($sceDelegateProvider) { $sceDelegateProvider.resourceUrlWhitelist(['self', /^https?:\/\/(cdn\.)?yourdomain.com/]); }]) 

Вот пример jsbin, где он работает для 1.2.0rc1: http://jsbin.com/olavok/144/edit


Pre 1.2: Для более старых версий (ref http://better-inter.net/enabling-cors-in-angular-js/ ) вам нужно добавить следующие 2 строки в конфигурацию:

 $httpProvider.defaults.useXDomain = true; delete $httpProvider.defaults.headers.common['X-Requested-With']; 

Вот пример jsbin, где он работает для версий до 1.2: http://jsbin.com/olavok/11/edit

ПРИМЕЧАНИЕ. Не уверен, что он работает с последней версией Angular.

ОРИГИНАЛ:

Также возможно переопределить запрос OPTIONS (был протестирован только в Chrome):

 app.config(['$httpProvider', function ($httpProvider) { //Reset headers to avoid OPTIONS request (aka preflight) $httpProvider.defaults.headers.common = {}; $httpProvider.defaults.headers.post = {}; $httpProvider.defaults.headers.put = {}; $httpProvider.defaults.headers.patch = {}; }]); 

Ваша служба должна ответить на запрос OPTIONS с такими заголовками:

 Access-Control-Allow-Origin: [the same origin from the request] Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request] 

Вот хороший документ: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server

В том же документе говорится

В отличие от простых запросов (см. Выше), «предполетные» запросы сначала отправляют заголовок запроса HTTP OPTIONS на ресурс в другом домене, чтобы определить, безопасно ли отправлять этот запрос. Запросы межсайтовых запросов предваряются таким образом, поскольку они могут иметь последствия для пользовательских данных. В частности, запрос предваряется, если:

Он использует методы, отличные от GET или POST. Кроме того, если POST используется для отправки данных запроса с помощью Content-Type, отличного от application / x-www-form-urlencoded, multipart / form-data или text / plain, например, если POST-запрос отправляет XML-нагрузку на сервер используя application / xml или text / xml, тогда запрос предваряется.

Он устанавливает пользовательские заголовки в запросе (например, запрос использует заголовок, например X-PINGOTHER)

Когда исходным запросом является Get без пользовательских заголовков, браузер не должен делать запрос параметров, который он делает сейчас. Проблема в том, что он генерирует заголовок X-Requested-With, который заставляет запрос Options. См. https://github.com/angular/angular.js/pull/1454 о том, как удалить этот заголовок

Это устранило мою проблему:

 $http.defaults.headers.post["Content-Type"] = "text/plain"; 

Если вы используете nodeJS-сервер, вы можете использовать эту библиотеку, она отлично работает для меня https://github.com/expressjs/cors

 var express = require('express') , cors = require('cors') , app = express(); app.use(cors()); 

и после того, как вы сможете npm update .

Вот как я исправил эту проблему на ASP.NET

  • Во-первых, вы должны добавить пакет nuget Microsoft.AspNet.WebApi.Cors

  • Затем измените файл App_Start \ WebApiConfig.cs

     public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.EnableCors(); ... } } 
  • Добавьте этот атрибут в class controllerа

     [EnableCors(origins: "*", headers: "*", methods: "*")] public class MyController : ApiController { [AcceptVerbs("POST")] public IHttpActionResult Post([FromBody]YourDataType data) { ... return Ok(result); } } 
  • Я смог отправить json в действие таким образом

     $http({ method: 'POST', data: JSON.stringify(data), url: 'actionurl', headers: { 'Content-Type': 'application/json; charset=UTF-8' } }).then(...) 

Ссылка: включение запросов перекрестного происхождения в ASP.NET Web API 2

Как-то я исправил это, изменив

Access-Control-Allow-Headers “Происхождение, X-запрошенный-С, Content-Type, Accept, Authorization”

в

Access-Control-Allow-Headers «Происхождение, контент-тип, принятие, авторизация»

Совершенно описан в комментарии пкозловского. У меня было рабочее решение с AngularJS 1.2.6 и ASP.NET Web Api, но когда я обновил AngularJS до 1.3.3, запросы не удались.

  • Решение для сервера Web Api заключалось в том, чтобы добавить обработку запросов OPTIONS в начале метода настройки (подробнее об этом в блоге ):

     app.Use(async (context, next) => { IOwinRequest req = context.Request; IOwinResponse res = context.Response; if (req.Path.StartsWithSegments(new PathString("/Token"))) { var origin = req.Headers.Get("Origin"); if (!string.IsNullOrEmpty(origin)) { res.Headers.Set("Access-Control-Allow-Origin", origin); } if (req.Method == "OPTIONS") { res.StatusCode = 200; res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", "GET", "POST"); res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", "authorization", "content-type"); return; } } await next(); }); 

Если вы используете API для REST API, вы можете сделать это, как показано ниже.

Вам не нужно менять реализацию веб-сервисов.

Я объясню для Jersey 2.x

1) Сначала добавьте ResponseFilter, как показано ниже.

 import java.io.IOException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; public class CorsResponseFilter implements ContainerResponseFilter { @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { responseContext.getHeaders().add("Access-Control-Allow-Origin","*"); responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); } } 

2), то в web.xml в объявлении сервлета джерси добавьте ниже

   jersey.config.server.provider.classnames YOUR PACKAGE.CorsResponseFilter  

Я сдался, пытаясь решить эту проблему.

У моего IIS web.config были соответствующие « Access-Control-Allow-Methods » в нем, я экспериментировал с добавлением настроек конфигурации в свой Угловой код, но после нескольких часов работы, пытаясь заставить Chrome вызвать междоменный веб-сервис JSON, Я разочарованно сдался.

В конце концов, я добавил немой веб-страницу обработчика ASP.Net, получил это, чтобы позвонить в веб-службу JSON и вернуть результаты. Он заработал 2 минуты.

Вот код, который я использовал:

 public class LoadJSONData : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string URL = "......"; using (var client = new HttpClient()) { // New code: client.BaseAddress = new Uri(URL); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Add("Authorization", "Basic AUTHORIZATION_STRING"); HttpResponseMessage response = client.GetAsync(URL).Result; if (response.IsSuccessStatusCode) { var content = response.Content.ReadAsStringAsync().Result; context.Response.Write("Success: " + content); } else { context.Response.Write(response.StatusCode + " : Message - " + response.ReasonPhrase); } } } public bool IsReusable { get { return false; } } } 

И в моем Угловом controllerе …

 $http.get("/Handlers/LoadJSONData.ashx") .success(function (data) { .... }); 

Я уверен, что есть более простой / более общий способ сделать это, но жизнь слишком короткая …

Это сработало для меня, и теперь я могу продолжить нормальную работу!

  • Как разместить динамический компонент в контейнере
  • Расширитель компонентов с декоратором базового classа
  • Угловая 2 Показать и скрыть элемент
  • Как получить объект Date из json Response в машинописном тексте
  • * ng Для запуска бесконечного цикла в угловом2
  • Как глубоко наблюдать массив в angularjs?
  • Определение глобальных констант в Angular 2
  • Angular2 - Обмен данными между компонентами с использованием сервисов
  • Как использовать select / option / NgFor для массива объектов в Angular2
  • Angular 4 - обновить метаtags динамически для Facebook (Открыть график)
  • Как использовать ResolveComponentFactory (), но со строкой в ​​качестве ключа
  • Interesting Posts

    Ffmpeg: Ошибка входа в трубу

    Как отключить состояние сеанса в ASP.NET MVC?

    JSON.NET как сериализатор OAP для WebAPI 2 и ODataMediaTypeFormatter

    Управление несколькими асинхронными соединениями NSURLConnection

    Удалить значок Spotlight из строки меню

    Аргументы командной строки Eclipse

    Как я могу построить 3D-плоскость в Matlab?

    Должен ли я компилировать сборки релизов с информацией об отладке как «полный» или «только pdb»?

    Все ли указатели данных одинакового размера на одной платформе для всех типов данных?

    Как скопировать / создать экземпляр производного classа из указателя в полиморфный базовый class?

    Почему в VB.Net есть экземпляр по умолчанию, но не на C #?

    Объединить данные панели для получения данных балансной панели

    Как запустить приложение Android из командной строки?

    Когда сетевой пакет TCP будет fragmentирован на уровне приложения?

    Узел: войдите в файл вместо консоли

    Давайте будем гением компьютера.