Rack CORS

## CORSとは

CORSとは```Cross Origin Resource Sharing```の略語で、日本語で表すと```オリジン間リソース共有```です。
ブラウザでは、セキュリティの観点から異なるオリジンを持つクライアント側からのHTTPリクエストを制限しています。
これを```同一オリジンポリシー```と言います。

https://qiita.com/att55/items/2154a8aad8bf1409db2b

例えば、```http://localhost:3001```でapiサーバーが立ち上がっており、```http://localhost:3000```でクライアント側のサーバーが立ち上がっている場合に、クライアント側からapi側にリクエストをすると、オリジンが異なるため以下のようなエラーが出力されます。

```
    Access to XMLHttpRequest at 'http://localhost:3001/api/v1/posts' from origin 'http://localhost:3000' has 
    been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```
レスポンスヘッダーに```Access-Control-Allow-Origin```がないため、ブラウザでは制限されます。
異なるオリジンからのHTTPリクエストの可能にするには、バックエンド側での設定が必要です。

## rack-cors
Railsでは、```rack-cors```を使うことによって、オリジン間リソース共有が可能になります。
なぜ```Rack```なのかというと、RailsがRackベースのアプリケーションであり、レスポンスを返す際にRackを噛ませているからです。
リクエストがあった場合、以下のような順番でWeb ServerからRails側までリクエストが送られます。
```
                    ********************
                           Rails
                    ********************
                    
                            ↑ ↓
                    
                    ********************
                           Rack
                    ********************
                    
                            ↑ ↓
                    
                    ********************
                         Web Server
                    ********************
```
```rack-cors```を導入すると、Rackからのレスポンスのレスポンスヘッダーの設定を変更することができます。

どのように変化するのか、Rackミドルウェアを作成してRackからのレスポンスの中身を確認してみます。
↓ Rack Middlewareについてはこちら

https://qiita.com/nishio-dens/items/8011842f50995f46eafe

今回はプロジェクトディレクトリ直下にミドルウェア用のファイルを作成します。

```sample_middleware.rb
    class SampleMiddleware
        def initialize(app)
            @app = app
        end
    
        def call(env)
            status, headers, body = @app.call(env)
            puts "-" * 15 + " " + "status" + " " + "-" * 15
            puts status
            puts ""
            puts "-" * 15 + " " + "headers" + " " + "-" * 15
            puts headers
            return [status, headers, body]
        end
    end
```
```@app.call(env)```によってRails側にリクエストが送られ、それに対するレスポンスが```status```, ```request```, ```body```に入ります。
```Rack```の設定ファイルである```config.ru```にミドルウェアを含めるようにします。
```config.ru
    # This file is used by Rack-based servers to start the application.
    
    require_relative "config/environment"
    require_relative "sample_middleware" # <= 追加
    
    use SampleMiddleware # <= 追加
    
    run Rails.application
    Rails.application.load_server
```
以上のように設定して、api側にリクエストしてみると以下のような結果を得られます。
```
    --------------- status ---------------
    200
    
    --------------- headers ---------------
    {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"0", "X-Content-Type-Options"=>"nosniff", 
    "X-Download-Options"=>"noopen", "X-Permitted-Cross-Domain-Policies"=>"none", "Referrer-Policy"=>"strict-origin-when-cross-origin", 
    "Content-Type"=>"application/json; charset=utf-8", "ETag"=>"W/\"04feffc04d2090381867383349de9809\"", 
    "Cache-Control"=>"max-age=0, private, must-revalidate", "X-Request-Id"=>"ecf605ef-ec11-4f73-afa1-49ad20be3820", "X-Runtime"=>"0.045787", 
    "Server-Timing"=>"start_processing.action_controller;dur=3.481201171875, sql.active_record;dur=11.874755859375, instantiation.active_record;dur=0.32275390625, process_action.action_controller;dur=20.947021484375", "Vary"=>"Origin"}
```
```statusコード```は```200```ですが、レスポンスヘッダーに```'Access-Control-Allow-Origin'```がないため、ブラウザでは制限されます。

```rack-cors```をインストールして、```config/initializers/cors.rb```を以下のように設定します。
```cors.rb
    # Be sure to restart your server when you modify this file.
    
    # Avoid CORS issues when API is called from the frontend app.
    # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
    
    # Read more: https://github.com/cyu/rack-cors
    
    Rails.application.config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins "*"
    
        resource "*",
          headers: :any,
          methods: [:get, :post, :put, :patch, :delete, :options, :head]
      end
    end
```
リクエストを送ると以下のような結果が得られます。
```
    --------------- status ---------------
    200
    --------------- headers ---------------
    {"Access-Control-Allow-Origin"=>"*", "Access-Control-Allow-Methods"=>"GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD", 
    "Access-Control-Expose-Headers"=>"", "Access-Control-Max-Age"=>"7200", "X-Frame-Options"=>"SAMEORIGIN", 
    "X-XSS-Protection"=>"0", "X-Content-Type-Options"=>"nosniff", "X-Download-Options"=>"noopen", "X-Permitted-Cross-Domain-Policies"=>"none",
    "Referrer-Policy"=>"strict-origin-when-cross-origin", "ETag"=>"W/\"04feffc04d2090381867383349de9809\"", "Cache-Control"=>"max-age=0, private, must-revalidate",
    "X-Request-Id"=>"e8085ff2-8ba0-4433-8548-41f1d525afc8", "X-Runtime"=>"0.056507", "Server-Timing"=>"start_processing.action_controller;dur=3.614501953125, sql.active_record;dur=8.42626953125, instantiation.active_record;dur=0.226806640625, process_action.action_controller;dur=16.123779296875", "Vary"=>"Origin"}
```
```Access-Control-Allow-Origin```, ```Access-Control-Allow-Methods```, ```Access-Control-Expose-Headers```の３つが新しく追加されています。
```"Access-Control-Allow-Origin"=>"*"```は、どのオリジンからのリクエストも許可することを意味しているためブラウザで制限されることはなく正常に動作します。

```rack-cors```を導入すると```cors.rb```の設定に基づいて、レスポンスヘッダーが変更されることがわかります。

## 参考にしたサイト

https://github.com/cyu/rack-cors

https://www.amazon.co.jp/%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88-Ruby-Rails-%E3%80%90%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88%E3%80%91-Perfect/dp/4297114623

https://www.rubyguides.com/2018/09/rack-middleware/

https://ja.javascript.info/fetch-crossorigin

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

https://developer.mozilla.org/ja/docs/Glossary/Origin

https://ja.javascript.info/fetch-crossorigin#ref-3078