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


264

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

Я не впевнений, це помилка в AngularJS чи мені потрібно щось налаштувати. З того, що я розумію, оболонка XHR не може зробити запит HTTP OPTIONS, тому схоже, що браузер намагається з'ясувати, чи "дозволено" спочатку завантажити актив перед тим, як виконати запит GET. Якщо це так, то чи потрібно мені встановити заголовок CORS (Access-Control-Allow-Origin: http://asset.host .. ) також з хостом активів?

Відповіді:


226

Запит OPTIONS - це жодна помилка AngularJS, ось як Cross-Origin Resource Sharing стандартний мандат зобов’язує браузери вести себе. Зверніться до цього документа: https://developer.mozilla.org/en-US/docs/HTTP_access_control , де в розділі "Огляд" написано:

Стандарт перехресного походження ресурсів працює за допомогою додавання нових заголовків HTTP, які дозволяють серверам описувати набір джерел, яким дозволено читати цю інформацію за допомогою веб-браузера. Крім того, для методів запиту HTTP, які можуть викликати побічні ефекти щодо даних користувачів (зокрема; для методів HTTP, відмінних від GET, або для використання POST з певними типами MIME). Технічна специфікація передбачає, що браузери "попередньо переглядають" запит, вимагаючи підтримуваних методів із сервера із заголовком запиту HTTP OPTIONS, а потім, після "затвердження" від сервера, відправляють фактичний запит із фактичним методом запиту HTTP. Сервери також можуть сповіщати клієнтів про те, чи слід надсилати "облікові дані" (включаючи файли cookie та дані HTTP-аутентифікації) із запитами.

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


23
@matsko Чи можете ви детальніше розглянути, що ви зробили для вирішення цього питання? Я зіткнувся з тією ж проблемою, згідно з якою запит AngularJS $resource POST генерує запит OPTIONS на моєму сервері ExpresskendS ExpressJS (на тому ж хості; але інший порт).
dbau

6
Для всіх тих, хто не має права голосу - неможливо надати точну настройку конфігурації для всіх веб-серверів там - відповідь у цьому випадку займе 10 сторінок. Натомість я посилався на статтю, яка містить більше деталей.
pkozlowski.opensource

5
Ви маєте рацію, що не можете призначити відповідь - вам потрібно буде додати заголовки до вашої відповіді OPTIONS, яка охоплює всі заголовки, на які запитує браузер, у моєму випадку, використовуючи chrome, це заголовки 'accept' і 'x -запитаний-з '. У хромі я зрозумів, що мені потрібно додати, переглянувши мережевий запит і побачивши, що просить хром. Я використовую nodejs / expressjs як резервну копію, тому я створив службу, яка повернула відповідь на запит OPTIONS, який охоплює всі необхідні заголовки. -1 тому що я не міг скористатися цією відповіддю, щоб зрозуміти, що робити, мусив сам це зрозуміти.
Ед Сайкс

2
Я знаю, що це 2+ років потому, але ... ОП багато разів посилається на GET-запити (акцент додано мною): «[...] запит, надісланий до файлу активів, є запитом OPTIONS ( це повинен бути GET запит ). » і «браузер намагається з'ясувати, чи дозволено спочатку завантажити актив перед тим, як виконати запит GET ». Як це не може бути помилка AngularJS? Чи не слід забороняти запити перед передпольотом для GET?
polettix

4
@polettix навіть GET-запит може запустити запит перед польотом у веб-переглядачі, якщо використовуються користувацькі заголовки. Перевірте "не дуже прості запити" у статті, яку я пов’язав: html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request . Знову ж таки, це механізм браузера , а не AngularJS.
pkozlowski.opensource

70

Для Angular 1.2.0rc1 + вам потрібно додати ресурсUrlWhitelist.

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


Попереднє 1.2: Для старих версій (див. 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


Це працювало на мене. Ось відповідна магія для сторони сервера з nodejs / express: gist.github.com/dirkk0/5967221
dirkk0

14
Це працює лише для GET-запиту, все ще не можу знайти рішення для POST-запиту на міждоменному домені.
Пнкт

2
Поєднання цієї відповіді з відповіддю user2304582 вище повинно працювати для POST-запитів. Ви повинні сказати своєму серверу, щоб він приймав POST-запити від зовнішнього сервера, який його надіслав
Charlie Martin

Це не схоже, що для мене це буде працювати самостійно ... так -1. Вам потрібно щось із тієї самої URL-адреси, що відповість на запит ВАРІАНТІВ як частина передперелітної перевірки. Чи можете ви пояснити, чому це спрацювало?
Ед Сайкс

1
@EdSykes, я оновив відповідь на 1,2 і додав робочий приклад jsbin. Сподіваємось, це повинно вирішити це за вас. Переконайтесь, що ви натискаєте кнопку "Запустити з JS".
JStark

62

ПРИМІТКА. Не впевнений, що він працює з останньою версією 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 = {};
}]);

1
Мені добре підходить Chrome, FF та IE 10. IE 9 і новіші версії слід перевірити на власних пристроях на ОС Windows 7 або XP.
DOM

3
Можна також встановити це лише методом $ ресурсу, а не глобально:$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
h7r

3
Чи є з цим улов? Це здається таким простим, і все ж відповідь так глибоко внизу? редагувати: взагалі не працював.
Себастіалонсо

Куди йде цей код? в app.js, controller.js або де саме.
Мурлідхар Фічадія

Цей код не робить нічого для отримання запиту. Я додав окреме правило для get також у своїй конфігурації
kushalvm

34

Ваша служба повинна відповісти на 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


1
Щоб детальніше ознайомитись із частиною [той самий ДОСТУП-КОНТРОЛЬ-ЗАПИТУВАННЯ із запиту], як я вже згадував в іншій відповіді, вам потрібно переглянути запит ВАРІАНТІВ, який додає ваш браузер. Потім додайте ті заголовки в службу, яку ви будуєте (або веб-сервер). У expressjs, що виглядав так: ejs.options (' ', функція (запит, відповідь) {response.header ('Access-Control-Allow-Origin', ' '); response.header ('Методи доступу-контроль-дозвіл " ',' GET, PUT, POST, DELETE '); response.header (' Access-Control-Allow-Headers ',' Type-Type, Authorization, accept, x-request-with '); response.send (); });
Ed Sykes

1
Коментар Еда Сайкса дуже точний, майте на увазі, що заголовки, що надсилаються у відповідь OPTIONS, та ті, що надсилаються у відповіді POST, повинні бути абсолютно однаковими, наприклад: Access-Control-Allow-Origin: * не є тим самим, що як Access- Control-Allow-Origin: * через пробіли.
Lucia

20

Той самий документ говорить

На відміну від простих запитів (обговорених вище), "попередньо просвічені" запити спочатку надсилають заголовок запиту HTTP OPTIONS до ресурсу іншого домену, щоб визначити, чи безпечний фактичний запит для надсилання. Запити між веб-сайтами попередньо відображаються таким чином, оскільки вони можуть мати значення для даних користувачів. Зокрема, попередньо перевіряється запит, якщо:

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

Він встановлює власні заголовки у запиті (наприклад, у запиті використовується заголовок, наприклад X-PINGOTHER)

Якщо оригінальний запит "Отримати без спеціальних заголовків", браузер не повинен робити запит "Параметри", який він робить зараз. Проблема полягає в тому, що він генерує заголовок X-Requested-With, який змушує запит Options. Див. Https://github.com/angular/angular.js/pull/1454 про те, як видалити цей заголовок



10

Якщо ви використовуєте сервер nodeJS, ви можете використовувати цю бібліотеку, для мене це добре працювало https://github.com/expressjs/cors

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

app.use(cors());

і після того, як ви можете зробити npm update.


4

Ось як я вирішив цю проблему на ASP.NET

  • По-перше, вам слід додати цілий пакет Microsoft.AspNet.WebApi.Cors

  • Потім змініть файл App_Start \ WebApiConfig.cs

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
  • Додайте цей атрибут у свій клас контролера

    [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(...)

Довідка: Увімкнення перехресних запитів у веб-API 2 ASP.NET


2

Якось я це виправив, змінивши

<add name="Access-Control-Allow-Headers" 
     value="Origin, X-Requested-With, Content-Type, Accept, Authorization" 
     />

до

<add name="Access-Control-Allow-Headers" 
     value="Origin, Content-Type, Accept, Authorization" 
     />

0

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

  • Рішенням для веб-сервера 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();
    });

Вище не працює для мене. Існує набагато простіше рішення, що дозволяє CORS використовувати для AngularJS з ASP.NET WEB API 2.2 та новіших версій. Отримайте пакет Microsoft WebAPI CORS з Nuget, потім у своєму конфігураційному файлі WebAPI ... var cors = new EnableCorsAttribute ("www.example.com", " ", " "); config.EnableCors (cors); Деталі на сайті Microsoft тут asp.net/web-api/overview/security/…
kmcnamee

0

Якщо ви використовуєте Джерсі для API REST, ви можете зробити наступне

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

Я поясню для 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, у декларації сервлетів у футболці додайте нижче

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
    </init-param>

0

Я відмовився намагатися виправити це питання.

Мій 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;
        }
    }
}

А в моєму кутовому контролері ...

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

Я впевнений, що існує простіший / загальніший спосіб зробити це, але життя занадто коротке ...

Це працювало для мене, і я можу продовжувати робити нормальну роботу зараз !!


0

Для проекту IIS MVC 5 / Angular CLI (так, я добре знаю, що ваша проблема пов'язана з Angular JS) з API, я зробив наступне:

web.config під <system.webServer>вузлом

    <staticContent>
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>
    <httpProtocol>
      <customHeaders>
        <clear />
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, atv2" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
      </customHeaders>
    </httpProtocol>

global.asax.cs

protected void Application_BeginRequest() {
  if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) && Request.HttpMethod == "OPTIONS") {
    Response.Flush();
    Response.End();
  }
}

Це повинно вирішити ваші проблеми як для MVC, так і для WebAPI без необхідності робити всі інші. Потім я створив HttpInterceptor в проекті Angular CLI, який автоматично додав у відповідну інформацію заголовка. Сподіваюся, це допоможе комусь, хто опинився в подібній ситуації.


0

Трохи пізно на вечірку,

Якщо ви використовуєте Angular 7 (або 5/6/7) і PHP в якості API і все ще отримуєте цю помилку, спробуйте додати наступні параметри заголовка до кінцевої точки (PHP API).

 header("Access-Control-Allow-Origin: *");
 header("Access-Control-Allow-Methods: PUT, GET, POST, PUT, OPTIONS, DELETE, PATCH");
 header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");

Примітка : потрібно лише те, що потрібно Access-Control-Allow-Methods. Але я вставляю тут ще два, Access-Control-Allow-Originі Access-Control-Allow-Headersпросто тому, що вам потрібно буде все це правильно встановити для того, щоб Angular App правильно спілкувався з вашим API.

Сподіваюся, що це комусь допоможе.

Ура.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.