# SiskでCORS（クロスオリジンリソース共有）を有効にする

Source: https://docs.sisk-framework.org/ja/docs/features/cors.html

Sisk には、サービスを公開する際に [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) の処理に役立つツールがあります。この機能は HTTP プロトコルの一部ではなく、W3C が定義した Web ブラウザ固有の機能です。このセキュリティ機構は、ウェブページがそのページを提供したドメインとは異なるドメインへリクエストを送ることを防止します。サービス提供者は特定のドメイン、あるいは単一のドメインに対してリソースへのアクセスを許可できます。

## 同一オリジン

リソースが「同一オリジン」とみなされるためには、リクエストに [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin) ヘッダーが含まれている必要があります:

```http
GET /api/users HTTP/1.1
Host: example.com
Origin: http://example.com
...
```

そして、リモートサーバーは要求されたオリジンと同じ値を持つ [Access-Control-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) ヘッダーで応答しなければなりません:

```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
...
```

この検証は **明示的** です。ホスト、ポート、プロトコルは要求されたものと同一である必要があります。例を確認してください:

- サーバーが `Access-Control-Allow-Origin` を `https://example.com` と返す場合:
    - `https://example.net` - ドメインが異なる。
    - `http://example.com` - スキームが異なる。
    - `http://example.com:5555` - ポートが異なる。
    - `https://www.example.com` - ホストが異なる。

仕様では、リクエストとレスポンスの両方のヘッダーに対して構文のみが許容されます。URL のパスは無視されます。ポートはデフォルトポート（HTTP は 80、HTTPS は 443）の場合は省略されます。

```http
Origin: null
Origin: <scheme>://<hostname>
Origin: <scheme>://<hostname>:<port>
```

## CORS の有効化

標準で、[ListeningHost](https://docs.sisk-framework.org/api/Sisk.Core.Http.ListeningHost.md) 内に [CrossOriginResourceSharingHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.md) オブジェクトが用意されています。

サーバーの初期化時に CORS を設定できます:

```csharp
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UseCors(new CrossOriginResourceSharingHeaders(
            allowOrigin: "http://example.com",
            allowHeaders: ["Authorization"],
            exposeHeaders: ["Content-Type"]))
        .Build();

    await app.StartAsync();
}
```

上記のコードは **すべてのレスポンス** に対して以下のヘッダーを送信します:

```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Headers: Authorization
Access-Control-Expose-Headers: Content-Type
```

これらのヘッダーは、エラーやリダイレクトを含む、ウェブクライアントへのすべてのレスポンスで送信する必要があります。

[CrossOriginResourceSharingHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.md) クラスには、[AllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigin.md) と [AllowOrigins](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigins.md) の 2 つの類似したプロパティがあることに気付くでしょう。1 つは単数形、もう 1 つは複数形です。

- **AllowOrigin** プロパティは静的です。指定したオリジンだけがすべてのレスポンスで送信されます。
- **AllowOrigins** プロパティは動的です。サーバーはリクエストのオリジンがこのリストに含まれているかを確認し、含まれていればそのオリジンに対するレスポンスで送信されます。

許可されたオリジンがリクエストに依存する場合、つまり **AllowOrigins** や以下で説明する [AutoAllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoAllowOrigin.md) の値を使用する場合、Sisk は `Vary: Origin` ヘッダーも送信し、キャッシュが別のオリジン用のレスポンスを再利用しないようにします。

### ワイルドカードと自動ヘッダー

代わりに、レスポンスのオリジンにワイルドカード (`*`) を使用して、任意のオリジンからのリソースアクセスを許可することもできます。ただし、認証情報（認可ヘッダー）を含むリクエストに対してはこの値は使用できず、この操作は [エラーになります](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors/CORSNotSupportingCredentials)。

この問題は、[AllowOrigins](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigins.md) プロパティで許可するオリジンを明示的に列挙するか、[AllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigin.md) の値に [AutoAllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoAllowOrigin.md) 定数を使用することで回避できます。このマジックプロパティは、リクエストの `Origin` ヘッダーと同じ値で `Access-Control-Allow-Origin` ヘッダーを設定します。

`AllowOrigin` と同様の動作で、送信されたヘッダーに基づいて自動的に応答するために、[AutoFromRequestMethod](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoFromRequestMethod.md) と [AutoFromRequestHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoFromRequestHeaders.md) を使用することもできます。

```csharp
using var host = HttpServer.CreateBuilder()
    .UseCors(new CrossOriginResourceSharingHeaders(
        
        // リクエストの Origin ヘッダーに基づいて応答
        allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
        
        // Access-Control-Request-Method ヘッダーまたはリクエストメソッドに基づいて応答
        allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],

        // Access-Control-Request-Headers ヘッダーまたは送信されたヘッダーに基づいて応答
        allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],

        exposeHeaders: [HttpKnownHeaderNames.ContentType, "X-Authenticated-Account-Id"],
        allowCredentials: true))
    .Build();
```

## CORS を適用する他の方法

[service providers](https://docs.sisk-framework.org/ja/docs/extensions/service-providers.md) を扱う場合、設定ファイルで定義された値を上書きできます:

```csharp
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UsePortableConfiguration(...)
        .UseCors(cors => {
            // 設定ファイルで定義されたオリジンを上書きします
            // file.
            cors.AllowOrigin = "http://example.com";
        })
        .Build();

    await app.StartAsync();
}
```

## 特定ルートでの CORS 無効化

`UseCors` プロパティはルートおよびすべてのルート属性で利用可能で、以下の例のように無効化できます:

```csharp
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    // GET /api/widgets/colors
    [RouteGet("/colors", UseCors = false)]
    public IEnumerable<string> GetWidgets() {
        return new[] { "Green widget", "Red widget" };
    }
}
```

## レスポンス内の値の置き換え

ルーターアクション内で値を明示的に置き換えたり削除したりできます:

```csharp
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    public IEnumerable<string> GetWidgets(HttpRequest request) {

        // Removes the Access-Control-Allow-Credentials header
        request.Context.OverrideHeaders.AccessControlAllowCredentials = string.Empty;
        
        // Replaces the Access-Control-Allow-Origin
        request.Context.OverrideHeaders.AccessControlAllowOrigin = "https://contorso.com";

        return new[] { "Green widget", "Red widget" };
    }
}
```

## プリフライトリクエスト

プリフライトリクエストとは、クライアントが実際のリクエストの前に送信する [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/OPTIONS) メソッドのリクエストです。

`OPTIONS` メソッドのリクエストがルートのパスと一致しても、`OPTIONS` 用に明示的にルートが設定されていない場合、Sisk サーバーは `200 OK` と適用可能な CORS ヘッダーで応答し、その後クライアントは実際のリクエストを続行できます。

- ルートが [RouteMethod.Options](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMethod.md)（例: `MapOptions`）で明示的にマッピングされている場合、そのルートが自動応答ではなくリクエストを処理します。
- パスに一致するルートに `UseCors = false` が設定されている場合でも、自動応答は送信されますが CORS ヘッダーは含まれません。

> [!NOTE]
> Sisk 1.7 以前では、自動プリフライト応答は常に CORS ヘッダーを含んでおり、`UseCors = false` のルートでも同様でした。

## グローバルに CORS を無効化する

これは実現できません。CORS を使用しないようにするには、設定しないでください。
