Edited at

Redmine APIでPOST/PUTリクエストメソッドを送信できない時の(強引な)対処方法

More than 1 year has passed since last update.

Redmineが好き過ぎてRedmineを題材にしたファンタジー小説書いてる8amjpです。


Redmine API

さて、Redmineは素晴らしいツールなんですが、画面がちょっと無機質で、初心者には取っ付きにくい……という印象があります。

そこで私は、もう少し取っ付きやすい画面を提供するため、Angularを使ってRedmine API経由でチケットの情報にアクセスするWebクライアントアプリを作ろうと考えました。

このRedmine API、とっても便利なんですよ。AngularのHTTPモジュールからGETメソッドでアクセスすれば、チケットの情報をJSONやXMLの形式で自由自在に取得できます。

さらには、POSTメソッドやPUTメソッドを使えば、チケットの作成や更新も簡単に……

……できないんですよ。

Redmine APIに、Chrome等のWebブラウザからPOST/PUTメソッドでリクエストを送信すると、必ずエラーになります。

えー。これじゃ読み取り専用じゃないですか。なんで?


なぜエラーになるのか

勉強してみましたよ。もう。

えーと、Redmine APIにアクセスする時など、別ドメインへのリクエストは、セキュリティ上の理由で厳しいルールが課せられます。

そのルールを規定したのがCross-Origin Resource Sharing、略してCORSと呼ばれるものです。

で、「シンプルではない」リクエスト(PUT等)の送信時は、安全性を確認するため、事前にOPTIONSメソッドでリクエストを送信し、正常な応答があれば続けてPUTメソッドを送信します。

これをプリフライトリクエストといって…………

と、頭の痛くなるような(でもセキュリティ上とても大事な)ルールに縛られながら、リクエストを送るわけなんですが。ここで大問題が発生です。

まず、ChromeやFirefoxといった主要なWebブラウザは、必ずプリフライトリクエストを送信します。仕様です。簡単にはオフにできません。

で、Redmine APIでは、このプリフライトリクエストに応答する術がありません。必ず404エラーを返します。

結果、POSTもPUTもできません。

どうせえっちゅうんだよーー!!

インターネットに公開されたサーバーでならわかるけどよー! イントラの内部でくらい自由にやらせてくれよーー!!

畜生めー!! ちくしょうめーーー!!!


解決方法

というわけで、Apacheにこの怒りをぶつけてやりましょう。

/apps/redmine/conf/ディレクトリにあるhttpd-app.confの末尾に、下記を追記します。

Header always set Access-Control-Allow-Origin "*" 

Header always set Access-Control-Allow-Methods "OPTIONS, PUT"
Header always set Access-Control-Max-Age "60"
Header always set Access-Control-Allow-Headers "Content-Type, X-Redmine-API-Key"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

これでPOSTもPUTも送信し放題。いやー、嬉しくて何度POST/PUTしたことやら。

詳しいことはさっきのMDNのページに書いてありますが、一応さらりと解説しておきますね。

1行目は、どのドメインからのアクセスを許可するかを指定します。

ワイルドカードを指定すればすべてのアクセスを受け付けますが、セキュリティの事を考えるとちゃんと指定すべきです。わかっちゃいるけど。

2行目は、どのメソッドを許可するかを指定します。GET/HEAD/POSTメソッドは既に許可されてるっぽいので、OPTIONSとPUTメソッドを追加で指定します。

3行目は、プリフライトリクエストの有効期間です。とりあえず60秒にしました。深い根拠はありません。

4行目は、どのリクエストヘッダを許可するかを指定します。Redmine APIでは「Content-Type」と「X-Redmine-API-Key」ヘッダは必須なので、それを指定しています。

5-7行目は、OPTIONSメソッドのリクエストがあったらオウム返しにステータスコード200を返します。ゆるゆる門番。

これで、長らく頭を悩ませていた問題Issueが、力技とは言えやっと解決Resolvedになりました。良かった良かった。

ま、解決できたのは、本家Redmineサイトのフォーラム内のこの記事のおかげなんですけどね。ありがとうございます。


おわりに

「わざわざこんな事しなくても、こうすれば簡単にPOST/PUTできるよー」などという情報を御存知でしたらぜひ教えてください。