(JavaScript / .Net)CORS問題にハマる
背景
HTML/JS + WebAPI(ASP.NET) 構成のWEBサイトを構築しています。HTML/JSはVS CodeでデバッグはLive Serverを、WebAPI側(HTTPハンドラ *.ashx)は Visual Studioで開発デバッグしています。
HTML/JS側ではデバッグURLは、http://127.0.0.1:5500/ となり、WebAPI側は、http://localhost:49614/ となっています。
ポートやホスト名が異なるのでこの場合クロスオリジンとなり、ブラウザはクロスオリジン(この場合WebAPI)側にアクセスできません。
(HTML/JSとWebAPIを同じハンドラ、例えば https://hoge.example/html と https://hoge.example/webapi とかに配置すれば同一オリジンのためJS側からWebAPIへのアクセスはセキュリティ上問題なくアクセスできます。)
クロスオリジンの場合でもJS側からのアクセスを許すにはWebAPI側で許可が必要となるんですが、JS側の要求の種類とIIS側の仕様(?)によりちょっとハマってしまいました。。
単純リクエストの場合
JS側からのアクセスが以下のを満たす場合、単純リクエストととなり、普通のHTTPシーケンスと同じようにWebAPI側にリクエストを投げて、レスポンスがかえってくるという形になります。
①HTTP メソッドが次のいずれか: GET / HEAD / POST
②リクエストヘッダーが、以下のいずれかであること。:
Accept
Accept-Language
Content-Language
Content-Type
③Content-Type ヘッダーが存在する場合、その値が次のいずれかであること:
application/x-www-form-urlencoded
multipart/form-data
text/plain
例えば次のようなGETで特段ヘッダを付け加えずアクセスする場合しているので、単純リクエストになります。
//fetchを使ってWEB API呼び出し requestURL =`http://localhost:49614/GetHogeApi.ashx?param1=${param1}¶m2=${param2}`; await fetch(requestURL) .then(response => { if (!response.ok){ throw new Error(`HTTP error! Status: ${response.status}`); } return response.json(); // レスポンスボディを JSON として解析する Promise を返す }) .then(data => { console.log('取得したデータ:', data); // JSON データを受け取ってここで何らかの処理 }); return ; }) .catch(error => { // エラーが発生した場合の処理 console.error('データの取得に失敗しました:', error); });
単純リクエストでアクセスをした場合、WebAPI側でクロスオリジン許可の設定をしていれば、レスポンスに Access-Control-Allow-Origin ヘッダが追加され、そこにアクセス元のオリジンが含まれていれば成功、そうでないとエラーとブラウザは解釈します。
WebAPI側でのクロスオリジン許可ですが、ASP.NETのHTTPハンドラの場合以下のようにします。
public class GetHogeApi : IHttpHandler { public void ProcessRequest (HttpContext context) { //どのオリジンでも無条件に許可の場合 * で指定 context.Response.Headers.Add("Access-Control-Allow-Origin" , "*"); //特定のオリジンからのアクセスを許可する場合はオリジンで指定 context.Response.Headers.Add("Access-Control-Allow-Origin" , "http://127.0.0.1:5500"); } }
なお、Web.configでもクロスオリジン許可が設定できます。以下のような感じです。
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="GET, POST,OPTIONS" /> <add name="Access-Control-Allow-Headers" value="Content-Type" /> </customHeaders> </httpProtocol> </system.webServer> </configuration>
単純リクエストでない場合(プリフライトリクエスト)
上述の単純リクエストの条件に当てはまらない場合は、ブラウザが本リクエストの前に、OPTIONS メソッドでサーバーに「このリクエストを送っても大丈夫か?」と事前確認のプリフライトリクエストは発生します。 具体的には、以下のような感じとなります。
1.ブラウザが OPTIONS リクエストを送信: ヘッダーに、本リクエストのメソッドやヘッダー情報を含める。
2.サーバーが許可するか応答: Access-Control-Allow-Origin などで許可オリジン、メソッド、ヘッダーを返す。
3.ブラウザが応答をチェック: 許可されていれば本リクエストを送信、許可されていなければエラー。
さて、プリフライトリクエストになることを知らず、以下のようなJSリクエストコードを書いてしまいました。
var param1 = "param1"; var param2 = "param2"; const requestURL = `requestURL =`http://localhost:49614/GetHogeApi.ashx`; await fetch(requestURL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ param1, param2 }) }) .then(res => { if (!res.ok) throw new Error("失敗"); return res.json(); }) .then(data => { //受け取った結果を処理 }) .catch(err => { document.getElementById("login-message").textContent = err.message; });
Content-Type が "application/json" なので単純リクエストにならず、プリフライトリクエストになりますね。
HTTPハンドラ側でCORSを設定していたんですが、どうやらハンドラ側のコードまで処理が到達していないことがわかりました。 おそらくOPTIONS が ASP.NET まで届かず、IISが処理してしまっているようです。
ということで、Web.config側でクロスオリジン許可するようにすれば、プリフライトリクエストも問題なくさばけるようになりました。
なお、当初、Web.Debug.config を作ってそこにCORS許可設定を入れていたんですが、Webサイトプロジェクトの場合は、Web.Debug.config ファイルは一切処理されないようで。。結局Web.configにCORS許可を入れる必要があるようです。
(Webアプリケーションプロジェクトの場合は発行時に Web.Debug.config が処理されるようですが、Visual StudioのF5での実行ではそうならないため、F5実行で Web.Debug.Config の設定を使うにはSlowCheetah 等の拡張機能を使う必要があるようです。)
そもそもですが、単純リクエストになるようにContent-Type を "application/json"をやめて、GETやPOSTで単純にデータ流すだけでもよさそうですね。
(ネットワーク)v6プラスで運用中のAtermがよくフリーズする
NECのAterm WG2600HS2を使っていますが、数日おきにフリーズする現象が発生していました。あるときにはLEDが全て消灯してるときなどもありました。
ネットワーク構成とは以下のような感じです。
ISP(VNE) ----- HGW(ブリッジ) ----- v6プラス対応ルータ --- PC
AtermQAのAtermと端末との接続が安定しません、つながりませんを見てみると、以下のような説明が。。
IPv4 over IPv6(v6プラス・transix等)対応のAtermをご使用の場合は、動作モードの自動判定をオフにして改善されるかご確認ください。
IPv4 over IPv6(v6プラス・transix等)対応機種は、自動判定でいったん動作モードが確定しても、その後定期的に自動判定を行う場合があります。
自動判定が定期的に行われ、回線が不安定に感じる場合は、以下の手順で自動判定を無効にしてください。
1.クイック設定Webの『基本設定』画面で「自動判定」をOFFに変更
2.動作モードで自動判定時に設定されていた動作モードを選択して設定を保存
この回答通り、自動判定をOFFにし動作モードをV6プラス固定にしたところ、フリーズ現象が発生しなくなりました。
どうやら、自動判定がフリーズの原因だったようです。便利な機能かもしれませんが、それゆえにこういったトラブルにもつながるんですね。。