nuxtのformからPOSTされたデータをgoのサーバで受け取る(私的メモ)

## nuxt.js(vueファイル)


Vuetifyのコンポーネントを活用し、formを作成、validationとresetボタンを実装しました。

```vue
<template>
  <v-form ref="form" v-model="valid">
    <v-text-field
      v-model="title"
      :counter="10"
      :rules="titleRules"
      label="Title"
      required
    ></v-text-field>

    <v-textarea
      v-model="description"
      :counter="100"
      :rules="descriptionRules"
      label="Description"
      required
    ></v-textarea>

    <v-textarea
      v-model="content"
      :counter="100"
      :rules="contentRules"
      label="Content"
      required
    ></v-textarea>

    <v-btn :disabled="!valid" color="success" class="mr-4" @click="submit">
      SUBMIT
    </v-btn>

    <v-btn color="error" class="mr-4" @click="reset">
      Reset Form
    </v-btn>
  </v-form>
</template>

<script>
export default {
  data: () => ({
    valid: false,
    title: '',
    titleRules: [
      (v) => !!v || 'Title is required',
      (v) => (v && v.length <= 20) || 'Title must be less than 20 characters',
    ],
    description: '',
    descriptionRules: [
      (v) => !!v || 'Description is required',
      (v) =>
        (v && v.length <= 100) ||
        'Description must be less than 100 characters',
    ],
    content: '',
    contentRules: [
      (v) => !!v || 'Content is required',
      (v) =>
        (v && v.length <= 100) || 'Content must be less than 100 characters',
    ],
  }),
  methods: {
    post() {
      this.$axios.$post('http://localhost:10000/articles', {
        title: this.title,
        desc: this.description,
        content: this.content,
      })
    },
    submit() {
      this.$refs.form.validate()
      if (this.valid) {
        this.post()
        alert('「' + this.title + '」を登録しました。')
        document.location = '/articles'
      }
    },
    reset() {
      this.$refs.form.reset()
    },
  },
}
</script>
```



validationに合格するとsubmitボタンの色が変化し、ボタンをクリックするとイベントが発火しsubmit()が呼ばれます
その中で、post()メソッドを呼び出し$axiosモジュールを使ってAPIサーバにPOSTメソッドをデータ付きで送信します。axiosはデフォルトでContent-Typeをapplication/jsonで通信します。





## go

```golang
# CORS対策
func withCORS(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		fn(w, r)
	}
} 

# muxにハンドラを登録している部分です(gorilla/muxを使用)
myRouter.HandleFunc("/articles", withVars(withDB(db, withCORS(createNewArticle)))).Methods("POST")



# 登録してるハンドラ定義部分です
func createNewArticle(w http.ResponseWriter, r *http.Request) {
	log.Println("called createNewArticle")
	
	# db接続
	d := GetVar(r, "db").(Database)
	db := d.init()
	
	# JsonをArticle構造体としてパース
	article := ParseJsonArticle(w, r)
	
	# 検証と保存
	if valid := article.validate(); valid {
		db.Create(&article)
		if db.NewRecord(article) {
			log.Println("新規articleの保存に失敗しました。")
		}
	}
}
```



フォームから適当なデータを送ってみると....

```bash
(一部)
Access to XMLHttpRequest at 'http://localhost:10000/articles' from origin 'http://localhost:3000' 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.
xhr.js:178 POST http://localhost:10000/articles net::ERR_FAILED
```

CORSポリシーでクライアントからサーバーへのリクエストがブロックされていると怒られています。
特にこの部分、
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

'Access-Control-Allow-Origin' ヘッダーはgoでハンドラの直前にチェインさせている関数(withCORS)で確かに付与しているハズ...



(。´・ω・)ん?

go側のログを見る限り、ハンドラ関数に到達すらしていない模様。



こんな記事がありました。
https://qiita.com/att55/items/2154a8aad8bf1409db2b



プリフライトリクエストとシンプルリクエストなんてものがあるようです。

Content-Type=''application/json"の場合、クロスオリジン(ドメインかポートがサーバとクライアントで違う)の場合、一旦OPTIONSメソッドが走り、安全確認できたら本メソッドが走るイメージかなと理解します。
https://github.com/gorilla/mux#user-content-handling-cors-requests
に沿って、サーバ側のコードを直します。

```golang
func articlesCORSHandling(w http.ResponseWriter, r *http.Request){
	# レスポンスヘッダーの追加
	w.Header().Set("Access-Control-Allow-Origin","http://localhost:3000")
	w.Header().Set( "Access-Control-Allow-Headers", "Origin, ContentType,")
	w.Header().Set( "Access-Control-Allow-Methods","GET, POST, OPTIONS" )

	# GETメソッド
	if r.Method == http.MethodGet {
		log.Println("called GET /articles")
		d := GetVar(r, "db").(Database)
		db := d.init()
		var articles Articles
		db.Find(&articles)
		json.NewEncoder(w).Encode(articles)
	}
	 
	# POST メソッド
	if r.Method == http.MethodPost {
		log.Println("called POST /articles")
		d := GetVar(r, "db").(Database)
		db := d.init()
		article := ParseJsonArticle(w, r)
		log.Println(article)
		if valid := article.validate(); valid {
			// log.Println("valid!!")
			db.Create(&article)
			if db.NewRecord(article) == false {
				log.Println("新規articleを保存しました")
			}else {
				log.Println("新規articleの保存に失敗しました。")
			}
		}
	}

}

func handleRequests() {
	r := mux.NewRouter()
	r.HandleFunc("/articles", withVars(withDB(db, articlesCORSHandling))).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
	r.Use(mux.CORSMethodMiddleware(r))
	log.Fatal(http.ListenAndServe(":10000", r))
}
```



withCORSを関数内部に統合させる形でハンドラを定義し登録してます。
再度、適当なデータを送信してみると...

```bash
2020/08/11 12:44:48 called POST /articles
2020/08/11 12:44:48 {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC <nil>} test test test}
2020/08/11 12:44:48 新規articleを保存しました
```

成功しました。