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

264

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

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

Теги:
cors
cross-domain
preflight

14 ответов

218
Лучший ответ

Запрос 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-запросом метод. Серверы также могут уведомлять клиентов о том, являются ли "учетные данные" (включая файлы cookie и HTTP-аутентификацию) должны быть отправлены с запросы.

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

  • 1
    Да, это то, что я думал, что происходит. Оказывается, мне нужно настроить заголовки доступа на хосте ресурсов, чтобы заставить это работать.
  • 23
    @matsko Можете ли вы рассказать, что вы сделали, чтобы решить эту проблему? Я сталкиваюсь с той же проблемой, когда POST- запрос AngularJS $resource генерирует запрос OPTIONS на мой внутренний сервер ExpressJS (на том же хосте, но на другом порту).
Показать ещё 8 комментариев
66

Для 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

  • 0
    Это сработало для меня. Вот подходящая магия для серверной части с помощью nodejs / express: gist.github.com/dirkk0/5967221
  • 14
    Это работает только для GET-запроса, но не может найти решение для POST-запроса для междоменного домена.
Показать ещё 8 комментариев
64

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

ORIGINAL:

Также возможно переопределить запрос 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.
  • 3
    В качестве альтернативы можно установить это только для метода $ resource, а не глобально: $resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
Показать ещё 8 комментариев
33

Ваша служба должна ответить на запрос 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
    Чтобы подробнее остановиться на части [тот же ACCESS-CONTROL-REQUEST-HEADERS from request], как я уже упоминал в другом ответе, вам нужно посмотреть на запрос OPTIONS, добавляемый вашим браузером. Затем добавьте эти заголовки в службу, которую вы создаете (или веб-сервер). В выражениях, которые выглядели так: ejs.options (' ', function (request, response) {response.header ('Access-Control-Allow-Origin', ' '); response.header ('Access-Control-Allow-Methods ',' GET, PUT, POST, DELETE '); response.header (' Access-Control-Allow-Headers ',' Content-Type, Authorization, accept, x-required-with '); response.send (); });
  • 1
    Комментарий Эд Сайкс очень точный, имейте в виду, что заголовки, отправленные в ответе OPTIONS, и заголовки, отправленные в ответе POST, должны быть точно такими же, например: Access-Control-Allow-Origin: * не совпадает с Access- Control-Allow-Origin: * из-за пробелов.
20

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

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

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

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

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

10

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

$http.defaults.headers.post["Content-Type"] = "text/plain";
  • 0
    Это также работает для меня.
  • 0
    Решил мою проблему! 10x
Показать ещё 1 комментарий
4

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

  • Сначала вы должны добавить пакет nuget 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(...)
    

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

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

Немного опоздал на вечеринку,

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

Надеюсь, это кому-нибудь поможет.

Приветствия.

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

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

У моего IIS web.config был соответствующий "Access-Control-Allow-Methods" в нем, я экспериментировал с добавлением настроек конфигурации в свой Angular код, но после нескольких часов работы, пытаясь заставить 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;
        }
    }
}

И в моем контроллере Angular...

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

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

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

0

Если вы используете 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 в объявлении сервлета джерси добавьте ниже

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

Совершенно описан в комментарии pkozlowski. У меня было рабочее решение с 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();
    });
    
  • 0
    Выше не работает для меня. Существует гораздо более простое решение для включения 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/…

Ещё вопросы

Сообщество Overcoder
Наверх
Меню