@erenyaaayger (izuku midoriya)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Reactからaxiosを使ってGoサーバへPOSTする

解決したいこと

Reactのフォームで入力した値をGoサーバにあるDBに保存する

発生している問題・エラー

フォームに入力した値を送信し、レスポンスデータを確認しても下記のように値が入ってきません。

name:, email:

goサーバ側のログを見ると下記はリクエストのheaderとbodyの内容です。

http.Header{"Accept":[]string{"application/json, text/plain, */*"}, "Accept-Encoding":[]string{"gzip, deflate, br"}, "Accept-Language":[]string{"ja,en-US;q=0.9,en;q=0.8"}, "Connection":[]string{"keep-alive"}, "Content-Length":[]string{"42"}, "Content-Type":[]string{"application/json"}, "Origin":[]string{"http://localhost:3000"}, "Referer":[]string{"http://localhost:3000/"}, "Sec-Ch-Ua":[]string{"\"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"108\", \"Google Chrome\";v=\"108\""}, "Sec-Ch-Ua-Mobile":[]string{"?0"}, "Sec-Ch-Ua-Platform":[]string{"\"Windows\""}, "Sec-Fetch-Dest":[]string{"empty"}, "Sec-Fetch-Mode":[]string{"cors"}, "Sec-Fetch-Site":[]string{"same-site"}, "User-Agent":[]string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"}}
&http.body{src:(*io.LimitedReader)(0xc00021e000), hdr:interface {}(nil), r:(*bufio.Reader)(nil), closing:false, doEarlyClose:true, mu:sync.Mutex{state:0, sema:0x0}, sawEOF:false, closed:false, earlyClose:false, onHitEOF:(func())(0x7ff7b6f66ea0)}
{"time":"2022-12-19T16:06:50.4575541+09:00","level":"WARN","prefix":"echo","file":"response.go","line":"56","message":"response already committed"}

POSTMANを利用した場合、POSTした値はDBにきちんとデータは入ってきたため、サーバ側の問題はなさそうです。
↓POSTMANでリクエストした際のログ
image.png

該当するソースコード

APP.js
import axios from "axios";
import { useState } from "react";
import './App.css';

axios.defaults.withCredentials = true;

function App() {
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");

  const changeEmail = (e) => {
    setEmail(e.target.value);
  }

  const changeName = (e) => {
    setName(e.target.value);
  }

  const handleSubmit = (e) => {    
    e.preventDefault();
    console.log(name)
    console.log(email)
    axios.post('http://localhost:1323/save', {
      name: "name",
      email: "email",      
    }).then((res)=>{
      console.log(res.data)
    }).catch((res)=>{
      console.log(res.data)
    })
  };

  return (
    <form onSubmit={(event) => handleSubmit(event)}>
    <label htmlFor="name">Name: </label>
    <br />
    <input type="text" id="name" name="name" onChange={changeName}/>
    <br />
    <label htmlFor="email">Email: </label>
    <br />
    <input type="text" id="email" name="email" onChange={changeEmail}/>
    <br />
    <input type="submit" defaultValue={"Submit"} />
  </form>
    
  );
}



main.go

package main

import (
	"database/sql"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	utils "myapp/util"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	_ "github.com/mattn/go-sqlite3"
)

func init() {
	utils.LoggingSettings("./test.log")
}

// sqlのDBを指すポインタとしてDbを宣言
var Db *sql.DB

type User struct {
	Name  string `json:"name" xml:"name" form:"name" query:"name"`
	Email string `json:"email" xml:"email" form:"email" query:"email"`
}


func main() {
	e := echo.New()

	// Debug mode
	e.Debug = true

	//----------------------------------------------------------------------------
	// Custom middleware
	//----------------------------------------------------------------------------
	// Stats
	s := NewStats()
	//Use関数でリクエストが飛んできたときに事前に決められた共通の処理ができる
	//Proccessはリクエスト情報を取得する
	e.Use(s.Process)
	e.GET("/stats", s.Handle) // Endpoint to get stats

	// Server header
	e.Use(ServerHeader)

	//CORS
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowCredentials: true,
		AllowOrigins:     []string{"http://localhost:3000"},
		AllowHeaders:     []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
	}))

	//log
	e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if err := next(c); err != nil {
                c.Error(err)
            }

            req_header := fmt.Sprintf("%#v", c.Request().Header)
            req_body := fmt.Sprintf("%#v", c.Request().Body)

            os.Stdout.Write([]byte(req_header + "\n"))
            os.Stdout.Write([]byte(req_body + "\n"))
            return next(c)
        }
    })

	//----------------------------------------------------------------------------
	// Custom middleware
	//----------------------------------------------------------------------------


	e.POST("/save", save)

	e.POST("/users", func(c echo.Context) error {
		u := new(User)
		if err := c.Bind(u); err != nil {
			return err
		}
		return c.JSON(http.StatusCreated, u)
	})
	// Start server
	e.Logger.Fatal(e.Start(":1323"))
}



// e.POST("/save", save)
func save(c echo.Context) error {
	// Get name and email
	name := c.FormValue("name")
	email := c.FormValue("email")

	
	u := &User{}
	u.Name = name
	u.Email = email

	Db, _ := sql.Open("sqlite3", "./test.sql")
	defer Db.Close()

	cmd := "INSERT INTO test_table(name,email)VALUES(?,?)"
	_, err := Db.Exec(cmd, u.Name, u.Email)
	if err != nil {
		log.Fatalln(err)
	}

	return c.String(http.StatusOK, "name:"+name+", email:"+email)
}


自分で試したこと

useStateを使用してフォームに入力した値を取得しております。
axiosのPOSTをする前でconsole.logでuseStateの中身が反映されているか確認はできております。
なので、axiosのPOSTの仕方の原因があるのでしょうか。。

ご教示いただけますと幸いです。

0 likes

2Answer

Comments

  1. @erenyaaayger

    Questioner

    コメントありがとうございます。
    POSTMANを使い、Go側にリクエストをしたさいは、問題なく値が入ってきます。
    (上記に追記いたしました。)
  2. 個々の値ではなくてJSON(?)リクエストの全体を見たら何か分かりそうじゃないでしょうか
  3. @erenyaaayger

    Questioner

    度々、コメントありがとうございます。

    logの画像を変更いたしました。
    リクエストの全体をみるというのはこれらの情報のことを指しますのでしょうか?
    理解不足ですいません。。

https://github.com/labstack/echo/blob/42f07ed880400b8bb80906dfec8138c572748ae8回/response.go#L56
エラーを出したのがここで、呼び出された時にすでに Committed = true だったことがわかります。
これになる条件が WriteHeader が複数回呼び出された時なので、何らかの原因で何度か呼び出されていることになります。

https://github.com/labstack/echo/blob/42f07ed880400b8bb80906dfec8138c572748ae8/context.go#L177
こちらのコメントより、

// A side-effect of calling global error handler is that now Response has been committed (sent to the client) and
// middlewares up in chain can not change Response status code or Response body anymore.

とあるので、Context.Error を呼び出した時点で恐らく一度 WriteHeader が呼び出されています。
これに対してmiddlewareの中で、

func(c echo.Context) error {
            if err := next(c); err != nil {
                c.Error(err)
            }
...

となってエラーが出た時に処理を止めてない(returnしていない)ので、

  • 一度エラーが出てレスポンスを出力した
  • その後後続として正常な処理をしようとした

のタイミングの WriteHeader で再エラーになってるのかなぁと思います。

元のaxiosからの呼び出しが何か間違っていたのは恐らくそうで、元のエラーが何かはわかりませんが、エラーを適切にハンドリングするように修正すればその内容がわかるようになるかと思います。

0Like

Your answer might help someone💌