AngularJS выполняет HTTP-запрос OPTIONS для кросс-исходного ресурса
Я пытаюсь настроить AngularJS для связи с ресурсом кросс-происхождения, где хост-ресурс, который доставляет мои файлы шаблонов, находится в другом домене, и поэтому запрос XHR, который выполняет угловое выполнение, должен быть междоменным. Я добавил соответствующий заголовок CORS на свой сервер для HTTP-запроса, чтобы выполнить эту работу, но он, похоже, не работает. Проблема в том, что при проверке HTTP-запросов в моем браузере (хром) запрос, отправленный в файл актива, является запросом OPTIONS (это должен быть запрос GET).
Я не уверен, является ли это ошибкой в AngularJS или мне нужно что-то настроить. Из того, что я понимаю, XHR-обёртка не может сделать HTTP-запрос OPTIONS, поэтому похоже, что браузер пытается выяснить, разрешено ли «загрузка» актива прежде, чем он выполнит запрос GET. Если это так, то мне нужно установить заголовок CORS (Access-Control-Allow-Origin: http://asset.host … ) Вместе с хостом-ресурсом?
- Угловой материал Не удалось найти основную тему углового материала
- Angular2 - ошибка, если не проверить, существует ли {{object.field}}
- Локальное хранение в Угловом 2
- Как передать объект из одного компонента в другой в Angular 2?
- Цепочка RxJS Наблюдаемые данные http в Angular2 с использованием TypeScript
- Как сделать директивы и компоненты доступными во всем мире
- Angular 2 аннотация @ViewChild возвращает неопределенные
- Как правильно импортировать операторы из пакета `rxjs`
- Проверка ошибки с наблюдаемыми в сервисах
- Как обращаться с кодами статуса http, отличными от 200 в Angular 2
- Как обновить / обновить с углового 4 до углового 5+
- Обработка токенов обновления с использованием rxjs
- Ошибка TypeScript в коде Angular2: не удается найти имя 'module'
Запрос 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) { .... });
Я уверен, что есть более простой / более общий способ сделать это, но жизнь слишком короткая …
Это сработало для меня, и теперь я могу продолжить нормальную работу!