3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援→開発戻り浦島太郎状態の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。

(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/htmlhttps://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}&param2=${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で単純にデータ流すだけでもよさそうですね。

参考:
Zenn:CORSの理解を深める
Web API に CORS 実装 (CORE)

(ネットワーク)v6プラスで運用中のAtermがよくフリーズする

NECAterm 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プラス固定にしたところ、フリーズ現象が発生しなくなりました。

どうやら、自動判定がフリーズの原因だったようです。便利な機能かもしれませんが、それゆえにこういったトラブルにもつながるんですね。。