WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

CORS 非対応の場合のエラーメッセージ

by WebSurfer 2024年4月2日 16:33

ブラウザの fetch API を使ってドメインが異なる Web API などに要求を出した時、Web API 側が CORS に対応していない場合のブラウザのエラーメッセージや Fiddler で見た時の要求・応答がどのようになるかを書きます。

検証に使ったのは、先の記事「Web API に CORS 実装 (CORE)」に書いた ASP.NET Core Web API アプリで、Web API の CORS を無効にして、ドメインが異なる MVC アプリから fetch API を使って Web API に要求を出ました。ブラウザは Windows 10 の Chrome 123.0.6312.86 と Edge 123.0.2420.65 です。

ブラウザのエラーメッセージは、ディベロッパーツールの Console タブを開くと見ることができ、Web API 側が CORS 非対応では以下のようになるはずです。赤枠が「単純リクエスト」の場合で、青枠が「プリフライトリクエスト」の場合です。(注: 「単純リクエスト」というのは古い CORS 仕様書の用語で、現在 CORS を定義している Fetch 仕様書 ではその用語を使用していないそうです)

ブラウザのエラーメッセージ

エラーメッセージを以下にコピペしておきます。文章中の 'https://localhost:44371/api/values' が Web API 側で 'https://localhost:44343' がブラウザ側です。

「単純リクエスト」の場合

Access to fetch at 'https://localhost:44371/api/values' from origin 'https://localhost:44343' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

「プリフライトリクエスト」の場合

Access to fetch at 'https://localhost:44371/api/values' from origin 'https://localhost:44343' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

「プリフライトリクエスト」の場合は "Response to preflight request doesn't pass access control check:" という文が追加されています。その文の有無で「プリフライトリクエスト」か否かが分かるはずです。

蛇足ですが、エラーメッセージに含まれる "If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled." を見て mode を no-cors に設定してもほとんどの場合解決にはなりませんので注意してください。詳しくは先の記事「fetch API の mode:"no-cors"」を見てください。

「単純リクエスト」と「プリフライトリクエスト」が必要になる場合の違いについて簡単に書いておきます。「単純リクエスト」になるのは、メソッドが GET, HEAD, POST のいずれかで、かつ、要求ヘッダが「CORS-safelisted request header (CORS セーフリストリクエストヘッダー)」の場合です。それ以外は「プリフライトリクエスト」が必要になります。(詳しくは、MDN のドキュメント「オリジン間リソース共有 (CORS)」を見てください)

例えば、JSON 文字列をコンテンツに含めて POST 送信する場合は要求ヘッダに Content-Type: application/json を含めると思いますが、その場合は「プリフライトリクエスト」が必要になります。


Fiddler で見た時の要求・応答は以下のようになります。CORS の要求側はブラウザの仕事で、プリフライトが必要か否かもブラウザが判断して、下の画像の赤枠で示した CORS に必要な要求を出してくれます。

「単純リクエスト」の場合

ブラウザは Origin を要求ヘッダに含めて送信しますが、応答ヘッダに Access-Control-Allow-Origin が含まれないということででエラーとなっています。

「単純リクエスト」の場合

「プリフライトリクエスト」の場合

OPTIONS メソッドを使って CORS に必要なヘッダを要求に含めて出しています。下の画像の赤枠部分を見てください。しかし、応答ヘッダに Access-Control-Allow-Origin が含まれないということでエラーとなっています。

「プリフライトリクエスト」の場合

上の「プリフライトリクエスト」の場合の画像で、応答が 405 Method Not Allowed、Allow: GET, POST となっています。これは ASP.NET Core Web API による応答と思われますが、なぜ Method Not Allowed となるのか、GET, POST でないとダメと言われるのかは調べ切れてません。想像ですが、CORS を有効にしないと OPTIONS メソッドが許可されないということではないかと思われます。


参考に、Web API 側が CORS に対応していて、ブラウザ側でデータの取得に成功する場合の要求・応答を Fiddler で見た画像も下に貼っておきます。

「単純リクエスト」の場合

単純リクエスト

「プリフライトリクエスト」の場合

Web API が CORS に対応しているので「プリフライトリクエスト」の応答ヘッダに Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin が含まれて返ってきます。

プリフライトリクエスト

ブラウザはそれを見て再度要求を出しデータを取得します。

データの取得

Tags: , , ,

JavaScript

ListView に thead と tbody 追加

by WebSurfer 2024年4月1日 13:11

ASP.NET Web Forms アプリ用の ListView コントロールからレンダリングされる html ソースの table 要素内に thead, tbody 要素を追加する方法を書きます。

Visual Studio のデザイナを使って ListView のテーブル形式のコードを自動生成させると aspx ファイルに以下のような LayoutTemplate が生成されます。まず、それに赤枠と青枠で示したような thead, tbody 要素を追記します。

aspx ファイル

さらに、自動生成されたコードでは親の table 要素に runat="server" 属性が付与されているのでそれを削除する必要があります。上の画像のコメントを見てください。

runat="server" 属性が付与されているとその要素はサーバーコントロールになります。親の table 要素がサーバーコントロールになると、ASP.NET が html ソースをレンダリングする際に追加した thead, tbody 要素は削除されてしまいますので。

結果の html ソースは以下のようになります。

結果の html ソース

runat="server" 属性を削除するとサーバー側のコードでその要素を操作(例えばプロパティを動的に設定するなど)できなくなりますが、それ以外の不都合はないと思います。

GridView の例は別の記事「GridView と thead, tbody, tfoot」に書きましたのでそちらを見てください。

Tags: , ,

ASP.NET

Razor クラスライブラリ

by WebSurfer 2024年3月20日 15:43

Razor Class Library (RCL) に ASP.NET Core MVC の Controller と View を実装し、それを ASP.NET Core MVC アプリから利用する例を書きます。

Razor クラスライブラリの利用

話の発端は Microsoft Q&A のスレッド「.NET MVCで参照登録したDLLのビューを使用する方法」でクラスライブラリ中のビューが見つからないという話です。それに対処する方法を検討した時の話を備忘録として残しておくことにしました。

ASP.NET Core MVC の cshtml ファイルは、Microsoft のドキュメント「ASP.NET Core での Razor ファイルのコンパイル」によるとデフォルトでビルド時及びデプロイ時に dll にコンパイルされるそうなので、.NET Framework 版の MVC のようなランタイムコンパイルだからダメということはなさそうです。

ただし、アクションがビューを返すときにビューの検出と呼ばれるプロセスが行われるそうで、Microsoft のドキュメント「ビューの検出ルール」に書いてあるように、既定のフォルダに当該ビューが存在しなければなりません。

Microsoft Q&A の話は、独立したクラスライブラリに MVC の Controller と View を実装した結果、MVC アプリがビューを見つけることができず、InvalidOperationException: The view 'Index' was not found いう例外がスローされたということです。

クラスライブラリではなくて、ASP.NET Core MVC 本体のプロジェクトに Controller, Model, View を実装すればそういう悩みは解消できますが、どうしても独立したライブラリを使わなければならない事情があるなら、Razor Class Library として作るのが良さそうです。

Razor Class Library は Razor Pages だけでなく MVC もサポートしており、設定によって Controller, View, Model を実装して、MVC アプリからそれらを呼び出すことができるそうです。

ということで、Microsoft のドキュメント「ASP.NET Core の Razor クラス ライブラリ プロジェクトを使用した再利用可能 UI の作成」とネットで見つけた記事「Working with Razor Class Libraries in ASP.NET Core」を参考にして作ってみました。

ソリューションの構成

上の画像の RazorClassLib が RCL プロジェクトで、RazorClassLibraryHost が MVC アプリのプロジェクトです。両方とも Visual Studio 2022 のテンプレートで作って、前者には Controllers, Models, Views フォルダを追加し、それらの中に Controller, Model, View クラスを実装しています。後者はテンプレートで作ったまま何も手を加えてません。

MVC アプリの実行結果がこの記事の一番上の画像で、MVC アプリから RCL の Controller / View を呼び出して結果を表示したものです。ビューが見つからないという問題は起こりません。

何も難しいことは無かったのですが、Visual Studio で RCL プロジェクトを作成する際に注意すべき点があって、それは[サポート ページとビュー]にチェックを入れることです。

[サポート ページとビュー]にチェック

上に紹介した Microsoft のドキュメントに "Select Support pages and views if you need to support views. By default, only Razor Pages are supported." と書いてありますが、デフォルト([サポート ページとビュー]にチェック無し)では Razor Pages も MVC もサポートされません。その下に書いてある "The Razor class library (RCL) template defaults to Razor component development by default. The Support pages and views option supports pages and views." が正しいです。

Microsoft のドキュメント「クラス ライブラリで ASP.NET Core API を使用する」の「MVC 拡張機能を含める」のセクションの説明によると以下の設定が必要とのことです。

  1. Microsoft.NET.Sdk.Razor SDK を使用する。
  2. true に設定された AddRazorSupportForMvc MSBuild プロパティ。
  3. 共有フレームワークの <FrameworkReference> 要素。

Visual Studio 2022 の RCL テンプレートで[サポート ページとビュー]にチェックを入れて作成したプロジェクトは上の要件を満たしていて、生成されるプロジェクトファイルの内容は以下のようになります。

<Project Sdk="Microsoft.NET.Sdk.Razor">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>
  
</Project>

その他、気が付いた点を以下に書いておきます。

MVC アプリから RCL に参照設定がされていれば、RCL の Controller は、MVC アプリのプロジェクトの Program.cs に含まれる builder.Services.AddControllersWithViews(); で DI コンテナに登録されるようです。なので、参照設定以外は何もしなくても、要求に応じて RCL の Controller が呼び出され応答が返ってきます。

MVC アプリから RCL の Razor Pages を呼び出す場合は、Program.cs に builder.Services.AddRazorPages(); と app.MapRazorPages(); を追加する必要があります。追加しないと見つからないというエラーになります。

MVC アプリに実装されている _Layout.cshtml が自動的に使われます。なので、MVC アプリの wwwroot に実装されている Bootstrap などの CSS や jQuery などの JavaScript も有効になります。

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar