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

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

新規登録して質問してみよう
ただいま回答率
85.48%
HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

3138閲覧

pythonCGIからjsonを返したい(クロスオリジン要求の解決)

meron-pan

総合スコア44

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/10/08 08:15

編集2018/10/09 15:04

前提・実現したいこと

pythonでcgiを作ってみたいと思い、実験としてajaxを使ってjavascriptでjsonファイルを送信し、それをオウム返しするcgiを作りました。
返ってきたデータをコンソールで確認しようとしたところエラーが発生しましたが、解決方法がわかりません。
使っているのは、python3.7と、javaScript、html5です。(開発環境はwindows(Python3.7インストール済み)、ブラウザはfireFoxで検証)
ローカルサーバーを立ち上げて実験しています。

ファイル構成
server/
├ cgi-bin/
│ └  server.py
├cgiserver
├ajax.js
└ index.html

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

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://127.0.0.1:8000/cgi-bin/server.py にあるリモートリソースの読み込みは拒否されます (理由: CORS ヘッダー ‘Access-Control-Allow-Origin’ が足りない)。[詳細] クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://127.0.0.1:8000/cgi-bin/server.py にあるリモートリソースの読み込みは拒否されます (理由: CORS 要求が成功しなかった)。[詳細]

該当のソースコード

cgiserver.py

python

1# -*- coding: utf-8 -*- 2 3import http.server 4 5http.server.test(HandlerClass = http.server.CGIHTTPRequestHandler)

server.py

python

1#!/usr/bin/python3 2# -*- coding: utf-8 -*- 3 4import io 5import os 6import sys 7import cgi 8import cgitb 9import urllib.request 10import json 11 12sys.stdin = io.TextIOWrapper(sys.stdin.buffer,encoding='utf-8') 13sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') 14sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') 15 16cgitb.enable() 17 18if os.environ['REQUEST_METHOD'] == 'POST': 19 form = cgi.FieldStorage() 20 print("Access-Control-Allow-Origin: *\n") 21 print('Content-Type: text/json; charset=utf-8\r\n') 22 print(form) 23 24

ajax.js

Javascript

1 2 3ajaxSend = function (json) { 4 console.log("a") 5 var xhr = new XMLHttpRequest(); 6 xhr.open('POST', 'http://127.0.0.1:8000/cgi-bin/server.py', true); 7 xhr.setRequestHeader('content-type', 'application/json'); 8 xhr.send(json); 9 ajaxGet(); 10} 11 12ajaxGet = function () { 13 var req = new XMLHttpRequest(); 14 try{ 15 if (httpRequest.readyState === XMLHttpRequest.DONE) { 16 if (httpRequest.status === 200) { 17 alert(req.responseText); 18 }else { 19 alert('リクエストに問題が発生しました'); 20 } 21 } 22 } 23 catch(e){ 24 alert('例外を補足:'+e.description); 25 } 26} 27 28json = { 29 "messages": 30 [ 31 { 32 "date": "2012/1/12 19:12", 33 "text": "こんばんは" 34 } 35 , 36 { 37 "date": "2012/1/11 19:12", 38 "text": "Hello" 39 } 40 ] 41} 42 43window.onload = function(){ 44 console.log(document.getElementById("ajaxButton")) 45 document.getElementById("ajaxButton").addEventListener('click',a) 46} 47 48a = function(){ 49 ajaxSend(json); 50}

index.html

html

1<!DOCTYPE html> 2<html lang="ja"> 3 4<head> 5 <meta charset="UTF-8"> 6 <title>Javascript試験用</title> 7 <script charset="UTF-8" type="text/javascript" src="ajax.js"></script> 8</head> 9 10<body> 11 <button id="ajaxButton" type="button">決定</button> 12</body> 13</html>

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wwbQzhMkhhgEmhU

2018/10/08 10:48

hello.pyみたいな簡単なプログラムは実行できる環境なのでしょうか?まずはそこからのような...
meron-pan

2018/10/08 11:06

ええと、それをどのように証明すればいいのかよく分からないのですが...。
meron-pan

2018/10/08 11:09

とりあえずコマンドプロンプトでpython cgiserver.pyと打って、動作しているのは確認しています。
wwbQzhMkhhgEmhU

2018/10/08 11:10

↑のURLにあるrun.pyを実行してみてください
meron-pan

2018/10/08 11:22

実行できますよ。ただ、どのように証明をすればいいのでしょうか?
wwbQzhMkhhgEmhU

2018/10/08 11:29

実行できたなら私の勘違いです。なんかブラウザかXMLパーサーが立ち上がっているように見えたので、パスや関連付けの問題かと思いました。すみませんでした。
meron-pan

2018/10/08 11:38

ブラウザが立ち上がっているのは間違いありません(fireFoxのコンソールで確認していますし、わざわざボタン付けて送信のタイミングコントロールしているので)。
meron-pan

2018/10/08 11:43

パスや関連付けの場合そもそもpythonが動きませんし、cgiからデータが返ってこなくなる(これも返って来ているのかは断定できませんが)ので問題はないと思うのですが、関連付けができていない場合でもpythonを動かすことは可能なのでしょうか?
wwbQzhMkhhgEmhU

2018/10/08 15:46

返事があるとは思ってませんでした。どこから話せばいいのかよく分かりませんが、まずはajaxでない普通のhello, worldレベルのhtmlを返すところから確認した方がいいと思います。ぶっちゃけ*.pyが何をキックしてるのか不明だし、そもそもhttpサーバも何か分からないし、サーバー側のserver.py確認したのか分からないし、クライアント側でデバッガ使えるのかも不明だし、そんなこんなを1つずつ聞く手間が面倒になってああ書きました。
guest

回答2

0

postされたjsonの値をcgi(python)で取得する場合
例)ajax,json形式でpostされた項目id,passwordとすると

import sys
import json

data = sys.stdin.read()
params = json.load(data)
id = params['id']
password = params['password']

で取得できます。

結果をjson形式で返すばあいは (resultsをjson形式で返す場合)
print("Content-type: application/json")
print("\n\n")
jsonstring = json.dumps(results)
print(jsonstring)
print('\n')

投稿2018/10/19 00:01

pu_mahalo

総合スコア10

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

上記のプログラムですが、私のローカル環境でも動かしてみました。
おそらく、1箇所だけではなく、数カ所に問題があるようです。

1点目

まず、現在出ているエラーですが、CORSが原因で出ていますので、この問題を解決する必要があります。
すでに調べられたかとは思いますが、CORSが原因のエラーは、JavaScriptが埋め込まれているドメイン(URL)とは別のURLにアクセスしようとした時に、セキュリティ上の理由で発生するエラーです。

参考: CORSまとめ - Qiita

おそらくindex.htmlの画面の動作確認を https://localhost:8000 で、されたのではないでしょうか。
この時、JavaScriptが http://127.0.0.1:8000 配下のURLにアクセスしようとすると、文字列上は、localhostと127.0.0.1で(ブラウザとしては)別ドメインと認識されてしまうため、エラーが発生します。

今回は、PythonでCGIを作る事が目的のようなので、ajax.jsで、書かれている以下の箇所は、

xhr.open('POST', 'http://127.0.0.1:8000/cgi-bin/server.py', true);

ではなく、一旦、

xhr.open('POST', '/cgi-bin/server.py', true);

で良いかと思われます。(それ以外の方法として、http://127.0.0.1:8000 でindex.htmlにアクセスして動作確認する、CORS用の設定をするなどありますが、割愛します。)

この方法で、本件の質問に記載されているエラーは発生しなくなるはずです。

2点目

次にserver.pyのコードですが、こちらにも問題があります。

server.pyの以下のコードですが、

form = cgi.FieldStorage()

今回は、JSONでアクセスされるため、cgi.FieldStorage()ではJSONを正しく読み込めないはずです。cgi.FieldStorage()は、どうも、HTMLフォームをPOSTした時に使用する関数のようです。Python側でエラーが発生しており、リクエストが来ても正しく処理できていないと思われます。

調べてみましたが、PythonのCGIでJSONを受け取ってそのまま返す時は、Pythonの標準入力から読み込むのが一般的なようですね。

参考までに私が書いてみたものを載せておきます。これは、文字列を受け取って文字列をそのまま返すだけの処理ですが、JSONもまた、ただの文字列なので、以下のような感じでechoサーバーが書けました。

#!/usr/bin/python3 # -*- coding: utf-8 -*- import os import sys import cgi if os.environ['REQUEST_METHOD'] == 'POST': length, _ = cgi.parse_header(os.environ['CONTENT_LENGTH']) data = sys.stdin.buffer.read(int(length)) print('Content-Type: text/json; charset=utf-8') print("Access-Control-Allow-Origin: *\r\n") print(data.decode("utf-8"))

CONTENT_LENGTH(リクエストの文字列の長さ)を取得して、その文字数分だけ、標準入力から読みこみ、その結果にヘッダを付けてそのまま投げ返しているだけです。(ちなみに、JSONとして読み込む場合は、JSONのパース処理が必要になります。)

ちなみに、JSONを返すAPIなどを作成される場合は、ブラウザでJavaScriptを実行してアクセスして動作確認よりもでAPI単体(この場合は、URLにPOSTで直接アクセスして)で動作確認した方が、問題の切り分けがしやすくなります。(JavaScriptが間違ってるのか、Pythonのコードがおかしいのか判断しやすくなります。)

API単体で動作確認するためのツールが色々あるので、使いやすそうなものを探して、使ってみて下さい。参考までに2点ほど上げておきます。(もちろん、curlコマンドで叩くというのもアリです。 curlコマンドを使ってHTTPSのAPIを叩く - Qiita )

3点目

ajax.jsに戻りますが、URLだけでなく、それ以外の箇所も(正常に動作させるなら)修正した方が良さそうです。この箇所に関しては、全てを説明するのは、非常に大変なので、割愛させていただいたいですが、要約すると、以下二点になります。

  • JavaScriptの非同期通信の処理の方法について調べられたらよいかと思われます。
  • fetchという割と便利な関数があるので、(古いブラウザで動かしたいというわけでなければ、)おすすめします。

参考までに以下のコードを載せておきます。

json = { "messages": [ { "date": "2012/1/12 19:12", "text": "こんばんわ" } , { "date": "2012/1/11 19:12", "text": "Hello" } ] } ajaxSend = function (json) { var url = "/cgi-bin/server.py"; var method = "POST"; var headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', }; var body = JSON.stringify(json); var mhb = {method, headers, body} fetch(url, mhb) .then(function(response) { return response.json(); }).then(function(responseJson) { alert(JSON.stringify(responseJson)); }).catch(console.error); } window.onload = function(){ console.log(document.getElementById("ajaxButton")) document.getElementById("ajaxButton").addEventListener('click',a) } a = function(){ ajaxSend(json); }

長文になってしまいましたが、少しでも理解の一助となれば幸いです。

投稿2018/10/18 16:11

yuwki0131

総合スコア160

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meron-pan

2018/10/24 06:05

回答ありがとうございます。pythonの標準モジュールのhttp.serverだと、どうやらcgi-binフォルダをサーバーにするようで、htmlファイルを同じ階層にすれば動きました。まさか、ファイルの位置で、URLが異なってしまうとは思わなかったです。cgi.FieldStorage()では、JSONを受け取れないのは、初めて知りました。 なんとか、Javascriptの方はwebにいろいろ情報が載っていたので、自力で修正できたんですが、pythonについては情報が少なかったのでとても助かりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問