経緯
React + Web API(C#)でアプリを開発しています。
長いことnugetパッケージのアップデートを放置()していたので、更新していました。
ある程度直ってきたので、Reactとの動作確認を行ったところ、GETメソッドは問題なく通信できるのに、POST(+ Preflight)はCORSエラーになってしまい、機能しなくなっていました。
出てきたエラー
Access to fetch at '' from origin 'https://localhost:3000' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
Cross-Origin Resource Sharing error: PrefilghtMissingAllowOriginHeader
前提条件
-
Web API (C#):
フレームワークは.Net Framework 4.8
Microsoft.AspNet.WebApi.Cors
はv5.2.4からv5.3.0にアップデート
CORSの設定は以下の3ヶ所App_Start/WebApiConfig.csusing System.Web.Http; namespace SampleApp { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // 省略 config.EnableCors(); } } }
Web.config<system.webServer> <!-- 省略 --> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" /> </customHeaders> </httpProtocol> </system.webServer>
各ApiController(共通)using System.Web.Http; using System.Web.Http.Cors; namespace SampleApp.Controllers { [EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)] public class DefaultController : ApiController { [HttpGet] public IHttpActionResult Get() { return Ok(); } } }
-
React:
パッケージのreact
のバージョンはv16.2.0
通信はJSのfetch
を使用。基本の形は以下の通り// GET, DELETE など fetch (url, { mode: 'cors', method: method, credentials: 'include', }) // POST, PUT, PATCH など fetch (url, { mode: 'cors', method: method, credentials: 'include', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: body, })
原因
Request HeaderのContent-Typeに'application/json'を指定しているのに、Access-Control-Allow-HeadersにContent-Typeを"明示的に"追加していないことが原因でした。
詳しいことは以下の記事に書いてあります。
今回のケースに則して要点をまとめると、
セーフリクエストヘッダーであるContent-Typeを、Access-Control-Allow-Headersに追加する必要はないが、Content-Typeが'application/json'の場合はセーフリクエストヘッダーではなくなるため、追加しなければいけない。
です。
修正
今回はWeb.config
と各ApiControllerの修正でよさそうです。
※ローカルだけがcross-originとなり、それ以外はsame-originとなるため、ローカルだけ解消したことを確認しています。
<system.webServer>
<!-- 省略 -->
<httpProtocol>
<customHeaders>
+ <add name="Access-Control-Allow-Origin" value="https://localhost:3000" />
+ <add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" />
+ <add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>
</httpProtocol>
</system.webServer>
using System.Web.Http;
using System.Web.Http.Cors;
namespace SampleApp.Controllers
{
- [EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]
public class DefaultController : ApiController
{
[HttpGet]
public IHttpActionResult Get()
{
return Ok();
}
}
}
蛇足1: EnableCorsAttributeは使えなかった
EnableCorsAttribute
を使う方も試しましたが、解決できませんでした。
using System.Web.Http;
+ using System.Web.Http.Cors;
namespace SampleApp
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
- config.EnableCors();
+ var cors = new EnableCorsAttribute(
+ "https://localhost:3000",
+ "Content-Type",
+ "GET, POST, OPTIONS, PUT, PATCH, DELETE");
+ cors.SupportsCredentials = true;
+ config.EnableCors(cors);
}
}
}
Web.config
と一緒に設定するとCross-Origin Resource Sharing error: MultipleAllowOriginValues
のCORSエラーになります。
ただ、Web.config
の重複している設定を消してもPOSTでエラーになるのは変わらないので、使わないことにしました。
蛇足2: Credentialsがtrueの場合、Originに"*"を設定しても無効になる
公式のドキュメントを見ていると、以下のような記述が見つかりました。
CORS 仕様では、SupportsCredentials が true である場合、origins を "*" に設定しても無効であることが示されています。
すべて、"*"でした、、!
静かに直しておきます。
呟き
コンソールのエラー読むと、ちゃんと書いてありますね、、
エラーはちゃんと読もう!(自戒)
発生した疑問
時間があったら調べて解決したい疑問。
-
Access-Control-Allow-
系の値は、大文字小文字の区別があるのか - ローカルでの開発の際、
Access-Control-Allow-Origin
に"https://localhost:3000"
としか書かずに、https://localhost:3001
からアクセスしたら弾かれるのか
→CORS的には弾きそうだけど、弾かれなかったのが不思議 -
Access-Control-Allow-Headers
などに*, Content-Type
みたいな指定はできるのか
→ChatGPTには無理だと言われた -
EnableCorsAttribute
を使う方はなぜ上手くいかなかったのか
→どこかで[EnableCors]
をまとめて指定できるのがEnableCorsAttribute
だと見た気がするけど、Web.config
と両立できないなら使えないのは困る - 各APIコントローラーにある
EnableCors
の指定は必要なのか
→今回のソースのような指定なら、Web.config
の設定だけで十分にも見える
参考