AngularJS는 출처 간 자원에 대한 OPTIONS HTTP 요청을 수행합니다.


264

템플릿 파일을 제공하는 자산 호스트가 다른 도메인에 있으므로 각도가 수행하는 XHR 요청이 도메인 간이어야하는 교차 출처 리소스와 통신하도록 AngularJS를 설정하려고합니다. HTTP 요청 이이 작업을 수행 할 수 있도록 적절한 CORS 헤더를 서버에 추가했지만 작동하지 않는 것 같습니다. 문제는 브라우저 (크롬)에서 HTTP 요청을 검사 할 때 자산 파일로 전송 된 요청이 OPTIONS 요청 (GET 요청이어야 함)이라는 것입니다.

이것이 AngularJS의 버그인지 또는 구성해야하는지 확실하지 않습니다. 내가 이해 한 바에 따르면 XHR 래퍼는 OPTIONS HTTP 요청을 할 수 없으므로 브라우저가 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 여기서 "개요"섹션에 다음과 같이 나와 있습니다.

Cross-Origin Resource Sharing 표준은 서버가 웹 브라우저를 사용하여 해당 정보를 읽을 수있는 원본 세트를 설명 할 수있는 새로운 HTTP 헤더를 추가하여 작동합니다. 또한 사용자 데이터에 부작용을 일으킬 수있는 HTTP 요청 방법 (특히 GET 이외의 HTTP 방법 또는 특정 MIME 유형의 POST 사용)에 사용됩니다. 이 사양은 브라우저가 HTTP OPTIONS 요청 헤더를 사용하여 서버에서 지원되는 메소드를 요청한 다음 서버에서 "승인"하면 실제 HTTP 요청 메소드를 사용하여 실제 요청을 전송하도록 브라우저가 요청을 "프리 플라이트"하도록 규정합니다. 서버는 또한 "신임 정보"(쿠키 및 HTTP 인증 데이터 포함)를 요청과 함께 보내야하는지 여부를 클라이언트에 알릴 수 있습니다.

서버 자체와 지원하려는 HTTP 동사에 따라 설정이 달라 지므로 모든 WWW 서버에서 작동하는 일반적인 솔루션을 제공하는 것은 매우 어렵습니다. 서버에서 보내야하는 정확한 헤더에 대한 자세한 내용 이 담긴이 훌륭한 기사 ( http://www.html5rocks.com/en/tutorials/cors/ ) 를 살펴 보시기 바랍니다.


23
@matsko이 문제를 해결하기 위해 무엇을했는지 자세히 설명해 주시겠습니까? AngularJS $resource POST 요청이 백엔드 ExpressJS 서버 (동일한 호스트에서 다른 포트)에 OPTIONS 요청을 생성하는 동일한 문제에 직면하고 있습니다.
dbau

6
모든 다운 투표자들 에게는 모든 웹 서버에 대해 정확한 구성 설정 을 제공하는 것이 불가능합니다 .이 경우 대답은 10 페이지를 차지합니다. 대신 자세한 내용을 제공하는 기사에 연결했습니다.
pkozlowski.opensource

5
당신은 대답을 처방 할 수 없다는 것이 옳습니다. 브라우저를 사용하여 브라우저가 요청하는 모든 헤더를 다루는 OPTIONS 응답에 헤더를 추가해야합니다. 크롬을 사용하면 헤더 '수락'과 'x -요청 된- '. 크롬에서는 네트워크 요청을보고 크롬이 요구하는 것을 확인하여 추가해야 할 것을 알아 냈습니다. 백업으로 nodejs / expressjs를 사용하고 있으므로 필요한 모든 헤더를 다루는 OPTIONS 요청에 대한 응답을 반환하는 서비스를 만들었습니다. -1이 답변을 사용하여 수행해야 할 작업을 파악할 수 없었기 때문에 직접 해결해야했습니다.
Ed Sykes

2
2 년 후인 걸 알지만 ... OP는 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 +의 경우 resourceUrlWhitelist를 추가해야합니다.

1.2 : 릴리스 버전에서는 escapeForRegexp 함수를 추가하여 더 이상 문자열을 이스케이프 할 필요가 없습니다. URL을 직접 추가 할 수 있습니다

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

하위 폴더에 **를 추가해야합니다. 1.2의 작동하는 jsbin은 다음과 같습니다. 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/]);
 }])

다음은 1.2.0rc1에서 작동하는 jsbin 예제입니다. http://jsbin.com/olavok/144/edit


1.2 이전 : 이전 버전 ( http://better-inter.net/enabling-cors-in-angular-js/ 참조 )의 경우 구성에 다음 두 줄을 추가해야합니다.

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

1.2 이전 버전에서 작동하는 jsbin 예제는 다음과 같습니다. http://jsbin.com/olavok/11/edit


이것은 나를 위해 일했습니다. 다음은 nodejs / express를 사용하여 서버 측에 적합한 마술입니다. gist.github.com/dirkk0/5967221
dirkk0

14
이것은 GET 요청에만 작동하지만 여전히 크로스 도메인에서 POST 요청에 대한 솔루션을 찾을 수 없습니다.
Pnct

2
이 답변을 위의 user2304582 답변과 결합하면 POST 요청에 작동합니다. 외부 서버로부터 POST 요청을 수락하도록 서버에 지시해야합니다
Charlie Martin

이것은 나에게 스스로 작동하는 것처럼 보이지 않습니다. 그래서 -1입니다. 비행 전 점검의 일부로 OPTIONS 요청에 응답하는 동일한 URL에 무언가가 필요합니다. 이것이 왜 작동하는지 설명 할 수 있습니까?
Ed Sykes

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의 방법으로 만 설정할 수 있습니다.$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
h7r

3
이것으로 어떤 캐치가 있습니까? 너무 간단 해 보이지만 답이 너무 깊습니까? 편집 : 전혀 작동하지 않았습니다.
Sebastialonso

이 코드는 어디로 갑니까? app.js, controller.js 또는 정확히 어디에.
Murlidhar Fichadia

이 코드는 get 요청에 대해 아무 것도 수행하지 않습니다. 내 설정에서뿐만 아니라 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
다른 답변에서 언급했듯이 [요청의 동일한 ACCESS-CONTROL-REQUEST-HEADERS] 부분을 자세히 설명하려면 브라우저가 추가하는 OPTIONS 요청을 확인해야합니다. 그런 다음 구축중인 서비스 (또는 웹 서버)에 해당 헤더를 추가하십시오. 다음과 같은 expressjs에서 : ejs.options ( ' ', function (request, response) {response.header ( 'Access-Control-Allow-Origin', ' '); response.header ( 'Access-Control-Allow-Methods ','GET, PUT, POST, DELETE '); response.header ('액세스 제어 허용 헤더 ','컨텐츠 유형, 권한 부여, 수락, x 요청 된 항목 '); response.send (); });
Ed Sykes

1
Ed Sykes 의견은 매우 정확합니다. OPTIONS 응답으로 전송 된 헤더와 POST 응답으로 전송 된 헤더는 정확히 동일해야합니다. 예를 들면 다음과 같습니다. Access-Control-Allow-Origin : *은 Access- Control-Allow-Origin : * 공백 때문에.
Lucia

20

같은 문서가 말합니다

위에서 설명한 간단한 요청과 달리, "미리 비행 된"요청은 실제 요청이 안전하게 전송되는지 확인하기 위해 먼저 다른 도메인의 리소스에 HTTP OPTIONS 요청 헤더를 보냅니다. 사이트 간 요청은 사용자 데이터에 영향을 줄 수 있으므로 이와 같이 프리 플라이트됩니다. 특히 다음과 같은 경우 요청이 프리 플라이트됩니다.

GET 또는 POST 이외의 메소드를 사용합니다. 또한 POST를 사용하여 application / x-www-form-urlencoded, multipart / form-data 또는 text / plain 이외의 Content-Type으로 요청 데이터를 보내는 경우 (예 : POST 요청이 서버에 XML 페이로드를 보내는 경우) application / xml 또는 text / xml을 사용하면 요청이 프리 플라이트됩니다.

요청에 사용자 정의 헤더를 설정합니다 (예 : 요청에 X-PINGOTHER와 같은 헤더가 사용됨)

원래 요청이 사용자 지정 헤더없이 가져 오기 인 경우 브라우저는 지금 요청하는 옵션을 요청하지 않아야합니다. 문제는 옵션 요청을 강제로 수행하는 헤더 X-Requested-With를 생성한다는 것입니다. 이 헤더를 제거하는 방법 은 https://github.com/angular/angular.js/pull/1454 를 참조 하십시오 .


10

이것은 내 문제를 해결했다.

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

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

참조 : 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

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

위의 내용은 저에게 효과적이지 않습니다. ASP.NET WEB API 2.2 이상에서 AngularJS와 CORS를 함께 사용할 수있는 훨씬 간단한 솔루션이 있습니다. Nuget에서 Microsoft WebAPI CORS 패키지를 가져온 다음 WebAPI 구성 파일에서 가져 오십시오. var cors = new EnableCorsAttribute ( "www.example.com", " ", " "); config.EnableCors (cors); 자세한 내용은 Microsoft 사이트 여기를 참조
kmcnamee

0

REST API에 Jersey를 사용하는 경우 다음과 같이 할 수 있습니다

웹 서비스 구현을 변경할 필요가 없습니다.

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 "이 포함되어 있는데, Angular 코드에 구성 설정을 추가하는 실험을했지만 크롬이 크로스 도메인 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

IIS MVC 5 / Angular CLI의 경우 (예, API가있는 Angular JS 문제) 잘 알고 있습니다. 다음을 수행했습니다.

<system.webServer>노드 아래의 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();
  }
}

그렇게하면 MVC와 WebAPI 모두에 대한 문제를 해결할 수 있습니다. 그런 다음 Angular CLI 프로젝트에서 관련 헤더 정보에 자동으로 추가되는 HttpInterceptor를 작성했습니다. 이것이 비슷한 상황에서 누군가를 돕기를 바랍니다.


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제대로 제대로 API 이야기를 위해 각도 App에서 설정하는 모든 이들의 필요합니다 간단하기 때문에.

이것이 누군가를 돕기를 바랍니다.

건배.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.