31
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CORSエラーで行った対応

Posted at

概要

簡単なツール作成を通してAngularの使い方を勉強をしています。
その過程で、APIと連携させようとすると、下記のようなCORSのエラーが発生したので
私が行った対応を記載します。

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://*****/api/company/get_detail.html にあるリモートリソースの読み込みは拒否されます (理由: CORS ヘッダー ‘Access-Control-Allow-Origin’ が足りない)。

前提

  1. 作成しているツールは、http://localhost:4200で動作している
  2. API(http://*****/api/company/get_detail.html)へのアクセスに成功すると、会社情報がJSON形式で返ってくる
  3. APIはJSONPに対応している

原因

  1. http://localhost:4200で動作しているツールから、別ドメインで動いているAPIにアクセスしようとしたため、
    同一生成元ポリシーに反しており、リクエストが制限された。
  2. http://localhost:4200からのクロスドメインアクセスがサーバ側で許可されていなかった。
  • エラーが発生していた時のコード
company.service.ts
export class CompanyService <T> {
  constructor(private http: Http) {}
  getCompanyDetail() : Promise<T> {
    let sendUrl = 'http://*****/api/company/get_detail.html';
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });
    return this.http.post(sendUrl , {}, options).map(res => res.json()).toPromise();
  }
}

私が行った対応

対応1 JSONPを使ってみる

AngularにJsonpクラスがあったので、使い方を知るためにJSONPでアクセスするようにしてみました。

company.service.ts
export class CompanyService <T> {
  constructor(private jsonp: Jsonp) {}
  getCompanyDetail() : Promise<T> {
    // コールバック関数は固定で「JSONP_CALLBACK」を指定
    let sendUrl = 'http://*****/api/company/get_detail.html?callback=JSONP_CALLBACK';
    return this.jsonp.get(sendUrl).map(res => res.json()).toPromise();
  }
}
company.ts
export class CompanyComponent implements OnInit {
  constructor(private companyService : CompanyService<{}>) {}
  ngOnInit() {
    this.getCompany();
  }
  getCompany() {
    this.companyService.getCompanyDetail()
    .then(res => console.log(res))
    .catch(err => console.log(err));
  }
}

JSONPを使うことで、クロスドメインの制約を受けずに手軽に実装できますが、
セキュリティなどの問題もあるので、JSONPを使う場合は一般に公開しても問題ないAPIへのアクセスにのみ使用した方が良さそうです。

参考:JSONPでのクロスドメインアクセス

対応2 CORSを利用してみる

参考:HTTP アクセス制御 (CORS)

クロスドメインアクセスを許可する設定をサーバ側に追加します。
ツール側は、Httpクラスを使うように修正。(JSONPに変更する前の状態に戻しました。)

company.service.ts
export class CompanyService <T> {
  constructor(private http: Http) {}
  getCompanyDetail() : Promise<T> {
    let sendUrl = 'http://*****/api/company/get_detail.html';
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });
    return this.http.post(sendUrl , {}, options).map(res => res.json()).toPromise();
  }
}

(1) Access-Control-Allow-Originを追加

conf
<Location /api/company>
  <IfModule mod_headers.c>
     # アクセスを許可するURLを指定
     Header set Access-Control-Allow-Origin "http://localhost:4200"
  </IfModule>
</Location>

この状態でAPIへアクセスしてみると、今度は下記のようなエラーが発生しました。

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://*****/api/company/get_detail.html にあるリモートリソースの読み込みは拒否されます (理由: CORS プリフライトチャンネルからの CORS ヘッダー ‘Access-Control-Allow-Headers’ にトークン ‘content-type’ が足りない)。

Access-Control-Allow-Headersの設定が必要なようです。

(2) Access-Control-Allow-Headersを追加

conf
<Location /api/company>
  <IfModule mod_headers.c>
     # アクセスを許可するURLを指定
     Header set Access-Control-Allow-Origin "http://localhost:4200"
     # 許可するリクエストヘッダの種類を指定
     Header set Access-Control-Allow-Headers "Content-Type"
  </IfModule>
</Location>

この設定を追加すると、APIへアクセス出来るようになりました。
ついでなので、許可するHTTPメソッドも追加してみました。

(3) Access-Control-Allow-Methodsを追加

conf
<Location /api/company>
  <IfModule mod_headers.c>
     # アクセスを許可するURLを指定
     Header set Access-Control-Allow-Origin "http://localhost:4200"
     # 許可するリクエストヘッダの種類を指定
     Header set Access-Control-Allow-Headers "Content-Type"
     # 許可するメソッドを指定
     Header set Access-Control-Allow-Methods "GET"
  </IfModule>
</Location>

POSTでアクセスすると、何らかのエラーが出る事を期待していたのですが、
正常にアクセスできてしまいました。設定は有効になっているようなのですが・・・

a.jpg

そもそも、Access-Control-Allow-Methodsを設定していなくてもアクセス出来ていたので、
POST、GET、プリフライトリクエストで使用されるOPTIONSは常に許可されているのか、
どこか設定が間違っていたのか、分からず・・・

許可する HTTP メソッドの種類を指定する
には、常に許可すると記載されているので、POSTやGETは指定しなくても許可されているのかな。

PUTでアクセスするようにツールを修正。

company.service.ts
export class CompanyService <T> {
  constructor(private http: Http) {}
  getCompanyDetail() : Promise<T> {
    let sendUrl = 'http://*****/api/company/get_detail.html';
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });
    return this.http.put(url, {}, options).map(res => res.json()).toPromise();
  }
}

APIへアクセスすると、PUTは許可していないので、期待していたエラーがでました。

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://*****/api/company/get_detail.html にあるリモートリソースの読み込みは拒否されます (理由: CORS ヘッダー ‘Access-Control-Allow-Methods’ にメソッドが見つからない)。

それにしても、Angular難しいです・・・。慣れるまで、まだまだ時間が掛かりそうです。

31
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?