Nuxt.js × Railsでアプリ作成 CORS policy エラーの解決

## やりたいこと
- Railsで作ったAPIに対してNuxt.js側から投稿する（[前回の記事](https://qiita.com/rearail/items/0141dd7c754c97f009e3)の続編です）

**開発環境**
>ruby 2.6.5
Rails 6.0.3.4
node v14.7.0
yarn 1.22.4
フロント（Nuxt.js）のローカルサーバーのport番号は`8000`
API（Rails）のローカルサーバーのport番号は`3000`

### 投稿画面のマークアップ
- sendボタンがクリックされたら、Postmanで投稿ができることを確認できたURI `http://localhost:3000/api/v1/posts` に `POSTリクエスト` を送るような処理を実装

![app.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657259/5c2473b7-803e-424f-ee60-8e0e8b8a8173.gif)

```vuejs:pages/index.vue
<template>
  <div class="container">
    <h4>新規投稿</h4>
    <div class="form-control">
      <span>Title</span>
      <input
        type="text"
        v-model="title"
      >
    </div>
    <div class="form-control">
      <span>Body</span>
      <input
        type="text"
        v-model="body"
      >
    </div>
    <div class="form-control">
      <button @click="postMessage">send</button>
    </div>
  </div>
</template>

<script>
export default {
  data: () => ({
    title: null,
    body: null,
  }),
  methods: {
    postMessage() {
      const url = 'http://localhost:3000/api/v1/posts';
      const post = {
        title: this.title,
        body: this.body,
      };
      this.$axios.post(url, { post })
        .then(res => console.log(res.status))
        .catch(error => console.log(error));
    },
  }
}
</script>

<style>
.container {
  width: 60%;
  max-width: 600px;
  margin: 0 auto;
}
.form-control {
  margin: 20px 0;
}
h4 {
  text-align: center;
}
span {
  width: 4em;
  display: inline-block;
}
input[type="text"] {
  width: calc(100% - 6em);
}
button {
  margin: auto;
  display: block;
}
</style>
```

### CORSでのエラーになる時の解決方法
- 上記で実装すると、console.logに下記が出力される

> Access to XMLHttpRequest at 'http://localhost:3000/' from origin 'http://localhost:8000' 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.

*なぜ正しくリクエストが送れていないのか？*
`結論`
Nuxt側（フロント側）からそのままURIを指定してしまうと **CORSの制限がかかっているため** 正しくリクエストが送れていない

#### CORSとは
> オリジン間リソース共有Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。
ウェブアプリケーションは、自分とは異なるオリジン (ドメイン、プロトコル、ポート番号) にあるリソースをリクエストするとき、オリジン間 HTTP リクエストを実行します。

[MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)より抜粋

### CORS (オリジン間リソース共有) の問題を解決する
[Nuxt公式ページ](https://ja.nuxtjs.org/faq/http-proxy/)に書いてある方法をそのまま実施します
※ 公式ページではnpmでインストールしていますが、今回はyarnで開発しているので下記コマンドを使います（npmやyarnのコマンドについては[こちら](https://qiita.com/rearail/items/859a717990b39779bb6c)も参照ください）

```
// @nuxtjs/proxyのインストール
$ yarn add @nuxtjs/proxy
```

```js:app/nuxt.config.js
modules: [
  '@nuxtjs/axios',
  '@nuxtjs/proxy'
],
proxy: {
  '/api': {
    target: 'http://localhost:3000',
    pathRewrite: {
      '^/api' : '/api/v1'
    }
  }
},
```
`pathRewrite` の `'^/api' : '/api/v1'` の記述は、 [@nuxtjs/proxyのgithub](https://github.com/nuxt-community/proxy-module)を参考にしています

上記では、vue側でリクエストとして指定している `/api` がつく接頭辞を `/api/v1` にリライト（書き換え）て、`http://localhost:3000` に送ってね、と指定しています

proxyやpathRewriteについての記述方法はこの[サイト](https://riptutorial.com/ja/webpack/example/30415/%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7%E3%81%AE%E4%BD%BF%E7%94%A8)も参考になりました

### Nuxt側の修正
上記でProxyを設定してCORSの問題を解決する設定をしたので、NuxtのファイルでリクエストをPOSTしている箇所を修正します

```js:pages/index.vue
// 上の部分は省略
  methods: {
    postMessage() {
      const url = '/api/posts';
      const post = {
        title: this.title,
        body: this.body,
      };
      this.$axios.post(url, { post })
        .then(res => console.log(res.status))
        .catch(error => console.log(error));
    },
  }
// 以下省略
```
`const url = '/api/posts';` と訂正する
※ Proxyの設定、pathRewriteの設定により、**`http://localhost:3000/api/v1/posts`** にリクエストして、と変換される

### Rails側の修正
```ruby:api/app/controllers/api/v1/posts_controller.rb
# 上の部分は省略
  private

  def set_post
    @post = Post.find(params[:id])
  end

  def post_params
    params.require(:post).permit(:title, :body)
  end
end
```
送られてくるパラメーターは、Nuxt側でこのようなオブジェクト型で指定しているため、`require(:post)`をする必要がある

```js:pages/index.vue
const post = {
  title: this.title,
  body: this.body,
};
```
※このオブジェクト名がhogeだったら、`require(:hoge)`となる