AngularJS, bir menşe kaynak için OPTIONS HTTP isteği gerçekleştirir


264

Şablon dosyalarımı teslim varlık ana bilgisayar farklı bir etki alanı ve bu nedenle açısal gerçekleştirir XHR isteği çapraz etki alanı olması gerekir nerede çapraz kaynak ile iletişim kurmak için AngularJS kurmaya çalışıyorum. HTTP isteğinin bu işi yapması için sunucuma uygun CORS başlığını ekledim, ancak işe yaramıyor gibi görünüyor. Sorun, tarayıcımdaki (krom) HTTP isteklerini incelediğimde, varlık dosyasına gönderilen isteğin bir OPTIONS isteği olması (bir GET isteği olması).

Bunun AngularJS'de bir hata olup olmadığından veya bir şey yapılandırmam gerektiğinden emin değilim. Anladığım kadarıyla XHR sarmalayıcı bir SEÇENEKLER HTTP isteği yapamaz, bu yüzden tarayıcı GET isteğini gerçekleştirmeden önce varlığı indirmek için "izin" olup olmadığını anlamaya çalışıyor gibi görünüyor. Bu durumda, varlık ana bilgisayarıyla da CORS başlığını (Access-Control-Allow-Origin: http://asset.host ... ) Ayarlamam gerekir mi?

Yanıtlar:


226

OPTIONS isteği hiçbir şekilde bir AngularJS hatası değildir, Cross-Origin Kaynak Paylaşımı standardı tarayıcıların davranmasını zorunlu kılar. Lütfen şu belgeye bakın: https://developer.mozilla.org/en-US/docs/HTTP_access_control , burada "Genel Bakış" bölümünde:

Kaynaklar Arası Kaynak Paylaşımı standardı, sunucuların bir web tarayıcısı kullanarak bu bilgileri okumasına izin verilen kaynak kümelerini tanımlamasına izin veren yeni HTTP üstbilgileri ekleyerek çalışır. Ayrıca, kullanıcı verileri üzerinde yan etkilere neden olabilecek HTTP istek yöntemleri için (özellikle GET dışındaki HTTP yöntemleri için veya belirli MIME türleriyle POST kullanımı için). Spesifikasyon, tarayıcıların bir HTTP OPTIONS istek üstbilgisi ile sunucudan desteklenen yöntemleri talep ederek isteği "ön kontrol etmesini" ve ardından sunucunun "onayını" aldıktan sonra asıl isteği gerçek HTTP istek yöntemiyle göndermesini zorunlu kılar. Sunucular ayrıca istemcilere "kimlik bilgilerinin" (Çerezler ve HTTP Kimlik Doğrulama verileri dahil) isteklerle gönderilip gönderilmeyeceğini bildirebilir.

Kurulum, sunucunun kendisine ve desteklemeyi düşündüğünüz HTTP fiillerine bağlı olarak değişeceğinden, tüm WWW sunucuları için çalışacak genel bir çözüm sağlamak çok zordur. Bir sunucu tarafından gönderilmesi gereken tam başlıklarla ilgili daha fazla ayrıntı içeren bu mükemmel makaleyi ( http://www.html5rocks.com/en/tutorials/cors/ ) ele geçirmenizi öneririm.


23
@matsko Bunu çözmek için ne yaptığınızı biraz açıklayabilir misiniz? Bir AngularJS $resource POST isteği arka uç ExpressJS sunucuma (aynı ana bilgisayar; ancak farklı bir bağlantı noktası) bir SEÇENEKLER isteği oluşturduğu aynı sorunla karşı karşıyayım .
dbau

6
Tüm aşağı oy verenler için - orada tüm web sunucuları için tam yapılandırma kurulumu sağlamak imkansızdır - bu durumda cevap 10 sayfa alacaktır. Bunun yerine daha fazla ayrıntı sağlayan bir makaleye bağlandım.
pkozlowski.opensource

5
Bir cevap yazamayacağınız konusunda haklısınız - OPTIONS yanıtınıza tarayıcının istediği tüm başlıkları kapsayan başlık eklemeniz gerekecek, benim durumumda, krom kullanarak 'kabul et' ve 'x başlıklarıydı. '-ile -requested. Chrome'da, ağ isteğine bakarak ve kromun ne istediğini görerek ne eklemem gerektiğini anladım. Ben gerekli olarak tüm başlıkları kapsayan SEÇENEKLER isteğine bir yanıt döndüren bir hizmet oluşturdu destekli olarak nodejs / expressjs kullanıyorum. -1 çünkü bu cevabı ne yapacağımı anlamak için kullanamadım, kendim bulmak zorundaydım.
Ed Sykes

2
Ben 2+ yıl sonra biliyorum, ama ... OP birçok kez GET istekleri ifade (benim tarafımdan eklendi): «[...] varlık dosyasına gönderilen istek bir OPTIONS isteği ( bir GET olmalıdır) istek ). » ve «tarayıcı, öğeyi GET isteğini gerçekleştirmeden önce indirmesine" izin verilip verilmediğini "bulmaya çalışıyor ». O zaman nasıl bir AngularJS hatası olamaz? GET için ön kontrol talepleri yasaklanmamalı mı?
polettix

4
@polettix GET isteği bile , özel başlıklar kullanılıyorsa tarayıcıda uçuş öncesi isteği tetikleyebilir . Bağladığım makaledeki "çok basit olmayan istekler" i kontrol edin: html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request . Bir kez daha AngularJS değil , tarayıcının mekanizmasıdır.
pkozlowski.opensource

70

Açısal 1.2.0rc1 + için bir resourceUrlWhitelist eklemeniz gerekir.

1.2: yayın sürümü onlar artık dizeleri kaçmak zorunda bir escapeForRegexp işlevi ekledi. URL'yi doğrudan ekleyebilirsiniz

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

alt klasörler için ** eklediğinizden emin olun. İşte 1.2 için çalışan bir jsbin: http://jsbin.com/olavok/145/edit


1.2.0rc: Hala bir rc sürümündeyseniz, Açısal 1.2.0rc1 çözümü şöyle görünür:

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

1.2.0rc1 için çalıştığı bir jsbin örneği: http://jsbin.com/olavok/144/edit


Ön 1.2: Eski sürümler için (ref http://better-inter.net/enabling-cors-in-angular-js/ ) yapılandırmanıza aşağıdaki 2 satırı eklemeniz gerekir:

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

1.2 öncesi sürümleri için çalıştığı bir jsbin örneği: http://jsbin.com/olavok/11/edit


Bu benim için çalıştı. İşte nodejs / express ile sunucu tarafı için uygun sihir: gist.github.com/dirkk0/5967221
dirkk0

14
Bu yalnızca GET isteği için çalışır, yine de web alanları arası POST isteği için çözüm bulamaz.
Pnct

2
Bu yanıtı yukarıdaki user2304582 yanıtıyla birleştirmek POST isteklerinde işe yarayacaktır. Sunucunuza, onu gönderen harici sunucudan POST isteklerini kabul etmesini söylemelisiniz
Charlie Martin

Bu benim için tek başına çalışacak gibi görünmüyor ... yani -1. Aynı URL'de, uçuş öncesi kontrolün bir parçası olarak SEÇENEKLER isteğine yanıt verecek bir şeye ihtiyacınız vardır. Bunun neden işe yarayacağını açıklayabilir misiniz?
Ed Sykes

1
@EdSykes, 1.2 için cevabı güncelledim ve çalışan bir jsbin örneği ekledim. Umarım bu sizin için çözmelidir. "JS ile Çalıştır" düğmesine bastığınızdan emin olun.
JStark

62

NOT: Angular'ın en son sürümüyle çalıştığından emin değilim.

ORİJİNAL:

OPTIONS isteğini geçersiz kılmak da mümkündür (yalnızca Chrome'da test edildi):

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 ve IE 10 ile benim için iyi çalışıyor. IE 9 ve altı Windows 7 veya XP makinelerinde yerel olarak test edilmelidir.
DOM

3
Alternatif olarak, bu yalnızca $ kaynak yöntemine değil küresel olarak $resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
ayarlanabilir

3
Bununla ilgili herhangi bir sorun var mı? Çok basit görünüyor ve yine de cevap çok derin bir konu mu? edit: Hiç çalışmadı.
Sebastialonso

Bu kod nereye gidiyor? app.js, controller.js veya tam olarak nerede.
Murlidhar Fichadia

Bu kod alma isteği için hiçbir şey yapmaz. Ben yapılandırma için de almak için ayrı kural ekledi
kushalvm

34

Hizmetinizin aşağıdaki OPTIONSgibi başlıklara sahip bir isteği yanıtlaması gerekir :

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]

İşte iyi bir doküman: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server


1
Başka bir cevapta bahsettiğim gibi, [aynı ERİŞİM-KONTROL-TALEP-BAŞLIKLARININ istek üzerine] bölümünü ayrıntılı olarak incelemek için tarayıcınızın eklediği SEÇENEKLER isteğine bakmanız gerekir. Ardından, bu başlıkları oluşturduğunuz hizmete (veya web sunucusuna) ekleyin. Şuna benzeyen ifadelerde: ejs.options (' ', işlev (istek, yanıt) {response.header ('Access-Control-Allow-Origin', ' '); response.header ('Access-Control-Allow-Methods ',' GET, PUT, POST, DELETE '); response.header (' Access-Control-Allow-Headers ',' İçerik Tipi, Yetkilendirme, kabul et, x ile istendi '); response.send (); });
Ed Sykes

1
Ed Sykes yorumu çok doğrudur, OPTIONS yanıtında gönderilen başlıkların ve POST yanıtında gönderilen başlıkların tam olarak aynı olması gerektiğini unutmayın, örneğin: Access-Control-Allow-Origin: *, Access- Control-Allow-Origin: * boşluklardan dolayı.
Lucia

20

Aynı belge diyor ki

Basit isteklerden farklı olarak (yukarıda tartışılmıştır), "ön ödemeli" istekler, gerçek isteğin gönderilmesinin güvenli olup olmadığını belirlemek için önce diğer etki alanındaki kaynağa bir HTTP OPTIONS istek başlığı gönderir. Siteler arası talepler, kullanıcı verileri üzerinde etkileri olabileceğinden bu şekilde ön ödemelidir. Özellikle, aşağıdaki durumlarda bir istek önceden belirtilir:

GET veya POST dışındaki yöntemleri kullanır. Ayrıca, POST istek verilerini application / x-www-form-urlencoded, çoklu bölüm / form verileri veya metin / düz dışında bir İçerik Türü ile göndermek için kullanılırsa, örneğin POST isteği sunucuya XML yükü gönderiyorsa application / xml veya text / xml kullanarak, istek önceden düzenlenir.

İstekte özel üstbilgiler ayarlar (örneğin, istek X-PINGOTHER gibi bir üstbilgi kullanır)

Orijinal istek Hiçbir özel üstbilgi olmadan al olduğunda, tarayıcı şimdi yaptığı Seçenekler isteğinde bulunmamalıdır. Sorun, Seçenekler isteğini zorlayan bir X-Requested-with üstbilgisi oluşturmasıdır. Bkz https://github.com/angular/angular.js/pull/1454 bu başlığı kaldırmak için nasıl


10

Bu sorunumu çözdü:

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

10

Bir nodeJS sunucusu kullanıyorsanız, bu kütüphaneyi kullanabilirsiniz, benim için iyi çalıştı https://github.com/expressjs/cors

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

app.use(cors());

ve sonra bir npm update.


4

ASP.NET'te bu sorunu giderdim yolu

  • İlk olarak, Microsoft.AspNet.WebApi.Cors nuget paketini eklemelisiniz

  • Ardından App_Start \ WebApiConfig.cs dosyasını değiştirin

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
  • Bu niteliği denetleyici sınıfınıza ekleyin

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {  
        [AcceptVerbs("POST")]
        public IHttpActionResult Post([FromBody]YourDataType data)
        {
             ...
             return Ok(result);
        }
    }
  • Bu şekilde eyleme json gönderebildim

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

Başvuru: ASP.NET Web API 2'de Kaynaklar Arası İstekleri Etkinleştirme


2

Bir şekilde değiştirerek düzelttim

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

için

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

0

Mükemmel pkozlowski yorumunda tarif. AngularJS 1.2.6 ve ASP.NET Web Api ile çalışma çözümü vardı ama AngularJS 1.3.3 için yükseltme zaman istekleri başarısız oldu.

  • Web Api sunucusu için çözüm, yapılandırma yönteminin başına OPTIONS isteklerinin işlenmesini eklemekti ( bu blog yazısında daha fazla bilgi ):

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

Yukarıdakiler benim için çalışmıyor. ASP.NET WEB API 2.2 ve sonraki sürümleri ile AngularJS ile CORS'yi etkinleştirmek için çok daha basit bir çözüm var. Microsoft WebAPI CORS paketini Nuget'ten alın, ardından WebAPI yapılandırma dosyanıza alın ... var cors = new EnableCorsAttribute ("www.example.com", " ", " "); config.EnableCors (CORS); Ayrıntılar burada Microsoft sitesinde bulunmaktadır asp.net/web-api/overview/security/…
kmcnamee

0

Jersey for REST API'lerini kullanıyorsanız aşağıdaki işlemleri yapabilirsiniz

Web hizmeti uygulamanızı değiştirmek zorunda değilsiniz.

Jersey 2.x için açıklayacağım

1) Önce aşağıda gösterildiği gibi bir ResponseFilter ekleyin

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) sonra web.xml'de, forma sunucu uygulaması bildiriminde aşağıdakileri ekleyin

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

0

Bu sorunu çözmeye çalışmaktan vazgeçtim.

IIS web.config dosyamda "Access-Control-Allow-Methods " Açısal koduma yapılandırma ayarları eklemeyi denedim, ancak Chrome'un bir etki alanları arası JSON web hizmetini çağırmasını sağlamaya çalıştıktan birkaç saat sonra, perişan vazgeçtim.

Sonunda, aptal bir ASP.Net işleyicisi web sayfası ekledim , benim JSON web hizmetini çağırmak ve sonuçları getirmek için. 2 dakika içinde çalışıyor ve çalışıyor.

İşte kullandığım kod:

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

Ve Açısal denetleyicimde ...

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

Eminim bunu yapmanın daha basit / daha genel bir yolu var, ama hayat çok kısa ...

Bu benim için çalıştı ve şimdi normal iş yapmaya devam edebilirsiniz !!


0

Bir IIS MVC 5 / Açısal CLI (Evet, sorununuzu Angular JS ile olduğunun farkındayım) API ile proje için aşağıdakileri yaptım:

<system.webServer>düğüm altında web.config

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

Bu, diğer tüm dolaşmak zorunda kalmadan hem MVC hem de WebAPI için sorunları düzeltmek gerekir. Daha sonra ilgili başlık bilgilerine otomatik olarak eklenen Açısal CLI projesinde bir HttpInterceptor oluşturdum. Umarım bu benzer bir durumda birine yardımcı olur.


0

Partiye biraz geç kaldım,

API olarak Açısal 7 (veya 5/6/7) ve PHP kullanıyorsanız ve hala bu hatayı alıyorsanız, bitiş noktasına (PHP API) aşağıdaki başlık seçeneklerini eklemeyi deneyin.

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

Not : Tek gereken şey Access-Control-Allow-Methods. Ancak, buraya diğer ikisini yapıştırıyorum Access-Control-Allow-Originve Access-Control-Allow-Headersbasitçe, Angular App'in API'nizle düzgün bir şekilde konuşabilmesi için bunların hepsinin düzgün bir şekilde ayarlanması gerektiğinden.

Umarım bu birine yardımcı olur.

Şerefe.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.