質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

axiosを用いたajax通信を行ったらcorsエラーが発生してしまった

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 491

study_111

score 75

前提・実現したいこと

axiosを用いて、ajax通信を行いたいです。

発生している問題・エラーメッセージ

デベロッパーツールのコンソールに以下のようなエラーが発生してしまいました。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/polls/check/' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.

該当のソースコード

App.Vue

<template>
  <div id="app">
    <h3>掲示板に投稿する</h3>
    <label for="name">ニックネーム:</label>
    <input id="name" type="text" v-model="name">
    <br>
    <label for="comment">コメント:</label>
    <textarea id="comment" v-model="comment"></textarea>
    <br>
    <button @click="createComment">コメントをサーバーに送る</button>
    <h2>掲示板</h2>

  </div>
</template>
<script>
import axios from 'axios';

export default {
  data(){
    return{
      name:'',
      comment:'',
    };
  },
  methods: {
    createComment(){
      axios.post(
        'http://127.0.0.1:8000/polls/check/',
        {
          name:this.name,
          comment:this.comment
        },
        {
          headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
          "Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token"
          }
        },
      )
      .then(response => {
        console.log(response);
      })
      .catch(error => {
        console.log(error);
      })
      console.log('hello')

      this.name = '';
      this.comment = '';


    }
  },
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>


Vies.py

def check(request):
    if request.method == "GET":
        print('ok')
        return redirect("polls:index")
    json_dict = json.loads(request.body)

    print(json_dict)

    json_str = json.dumps(json_dict, ensure_ascii=False, indent=2)
    return HttpResponse(json_str)

試したこと

Django側では、こちらこちらのサイトを参考に、pip install django-cors-headersを行いsettings.pyを以下の部分を追記しました。

INSTALLED_APPS = [
    'corsheaders',
]
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
]
CORS_ORIGIN_WHITELIST = (
   'http://localhost:8080',
   'http://127.0.0.1:8080',
   'http://127.0.0.1:8000',
)
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_CREDENTIALS = True


また、App.vueファイルの以下hedder部分を削除した状態で、postするとPOST http://127.0.0.1:8000/polls/check/ 403 (Forbidden)とエラーが発生してしまっている状況です。

    {
          headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
          "Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token"
          }
        },

補足情報(FW/ツールのバージョンなど)

エディター:VScode,OS:Mac OS

どなたか、エラー解決の為、アドバイス頂けましたら幸いです。

追記

Qiita記事を参考に、axiosにwithCredentials: trueを追記しましたがPOST http://127.0.0.1:8000/polls/check/ 403 (Forbidden)が表示されている状態です。

  axios.post(
        'http://127.0.0.1:8000/polls/check/',
        {
          name:this.name,
          comment:this.comment
        },
        { 
          withCredentials: true
        },

      )

追記2

こちらの記事を参考に以下の部分をApp.vueに追記しましたが、エラーは解決されていない状況です。

import axios from 'axios';
const headers = {"X-CSRFTOKEN": "<csrf_token_very_long_string_goes_here>"}


axios.postメソッドには、 {headers: headers},を引数に追加しました

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • SurferOnWww

    2020/07/18 11:53 編集

    エラーメッセージが Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response. と言ってますけど、そのあたりは調べましたか?

    キャンセル

  • study_111

    2020/07/18 12:56 編集

    エラー内容から、Qiita記事を参考にコードを追記しました。
    django側ではdjango-cors-headersをインストールすれば良いみたいなので、もしかしたらフロント側の修正がどこかで必要かもしれないですね
    https://stackoverflow.com/questions/45256403/django-angular-cors-error-access-control-allow-origin-not-allowed

    キャンセル

  • SurferOnWww

    2020/07/18 13:22

    普通にユーザーがブラウザを使ってアクセスするケースで、プリフライトリクエスト(エラーメッセージからそうであろうと想像)になる場合は、以下の記事の画像(Fiddler によるキャプチャ画像)の左下のウィンドウに示すように HTTP 動詞が OPTION になって、赤枠で囲ったヘッダが送信されるはずです。

    http://surferonwww.info/BlogEngine/post/2016/12/27/wcf-service-in-cross-domain.aspx

    それを受けて、画像の右下のウィンドウの赤枠で囲ったようなヘッダが返ってくるはずです。

    エラーメッセージの "Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response." と話が合わないのが解せませんが、質問者さんの方でも Fiddler を使って要求・応答ヘッダーがどうなっているか調べてはいかがですか。

    axios とか全く知らないので、もし的外れのレスでしたらすみません。

    キャンセル

回答 2

checkベストアンサー

+2

追記:Vue 公式ドキュメントによって、フロントエンドとバックエンドのサーバーが同一ではない場合、設定の devServer.proxy にバックエンドサーバーのアドレスを入れれば、フロントエンドで知らないリクエストを全部バックエンドに渡せます。

// vue.config.js
module.exports = {
  devServer: {
    proxy: 'http://localhost:8000'
  }
}

そうしたら、リクエストの際にアドレスを明言する必要がなくなります。

      axios.post(
        '/polls/check/',
        {
          name:this.name,
          comment:this.comment
        },
      )

追記:そうしてもCSRFを踏んだら、Django側その関数に @csrf_exempt でマークしてください

@csrf_exempt
def check():
    pass

AkitoshiManabeさんの言う通り、http://127.0.0.1:8080 と http://localhost:8080との違いで Cross Origin エラーが出てます。

こちらのコードから推測できる誤解を解けておきましょう:

      axios.post(
        'http://127.0.0.1:8000/polls/check/',
        {
          name:this.name,
          comment:this.comment
        },
        {
          headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
          "Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token"
          }
        },
      )

上記の axios の headers はクライアントからサーバーへのリクエストの headers です。例えば、Cookie や Accept など、クライアントができることを示すためです。

Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers、この三つの headers はサーバーができることですから、サーバーからクライアントへのレスポンスについてるものです。この三つの headers は axios の headers に入れても意味がありません。削除していいです。

CORS_ORIGIN_WHITELIST = (
   'http://localhost:8080',
   'http://127.0.0.1:8080',
   'http://127.0.0.1:8000',
)

上記の設定をしたら、上記の headers は Django からのレスポンスに CorsMiddleware によって自動的についてるはずですが、カッコを使ってしまい list ではなく tuple を使っていますので、どんな反応してるかわかりません。カッコ () をブラケット [] で書き換えてみたらいかがでしょうか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/23 18:52

    ご追記ありがとうございます。
    「 @csrf_exempt 」をつけたことで、csrfによるエラーが解消されました。
    長々とご親切に教えて頂きまして、ありがとうございました。

    キャンセル

  • 2020/07/23 19:16

    僕も経験浅くて、色々勉強になりました。これはあくまで開発ための一時的なもので、デプロイの際には色々修正しなければなりません。他に webpack を使ってまともに Django から Vue フロントエンドを表示してる方法もありますので、ご時間があればそちらの方も探してみてください。

    キャンセル

  • 2020/07/23 21:29 編集

    デプロイの際は、開発用の設定のままになっていないように注意したいと思います。
    ご親切にご助言下さり、ありがとうございました。
    今後も、教えて貰ったことを生かし、勉強を続けていきたいと思います。

    キャンセル

+1

オリジン は 「プロトコル + // + ホスト名 + ポート番号」で成る文字列です。

ブラウザは http://127.0.0.1:8080 と http://localhost:8080 とはクロスオリジンの関係にあるものとして、CORS エラーを吐きます。

エラー解決の為、アドバイス頂けましたら幸いです

他者の記事やコードを真似て、手を動かして確認できているように感じますが、
それぞれの仕組み(なぜ、そのように書けるのか?)について、詳細を再確認してください

活字ばかりだと退屈かもしれませんが、仕組みを覚えると、エラー原因は自身で特定できるようになります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/18 19:01 編集

    ご返信ありがとうございます。
    > 1. サーバー-ブラウザ間で事前にトークン(合言葉)を示し合わせる。
    こちらなのですが、実際に運用していく目的であります為、ランダムに一意に生成されるトークンの生成を考えています。
    また、サーバーとブラウザ間で示し合せる為には、フロント側で生成したcsrfトークンをDjango側で検証する必要かと思うのですが、今回djangoのテンプレートエンジンを使用し{% csrf_token %}のような形で生成している訳ではない為、実際にどのようにブラウザから送られてきたトークンを検証するのかといった部分が分からない状態ですね。。

    キャンセル

  • 2020/07/18 19:26

    > ブラウザから送られてきたトークンを検証する
    サーバー側で生成してブラウザに渡した情報ですので、セッションとしてサーバー側で保持しておく。
    POST時にデータ保存前に比較する(比較後はセッションとの紐付けを解除)。という形で検証できそうですよね。

    ご質問の本題であるCORSから脱線して、CSRF、Sessionの話題に変わっていますので、色々と試してみて、改めて質問にまとめてみてはいかがでしょう。

    キャンセル

  • 2020/07/19 01:21 編集

    そうですね。もう少し色々と調べてみて、新たに回答がつかなかった場合、質問を新たに立てるか考えたいと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る