1
votes

Client side(React/axios.post) failed to post to server side api (Golang/gin) with status code 404. I want to make this post request success.

Following curl success to write data in mysql table

curl -X POST -H "Content-Type: application/json" -d '{"title":"bbb", "content":"bbb"}' localhost:4000/api/post

But, in case of axios.post, 404 error occurs.

This is target source code.

interface ArticleState {
  title: string;
  content: string;
  redirect: boolean;
}

class Post extends React.Component<{}, ArticleState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      title: '',
      content: '',
      redirect: false,
    };

    this.handleChangeTitle = this.handleChangeTitle.bind(this);
    this.handleChangeContent = this.handleChangeContent.bind(this);
    this.setRedirect = this.setRedirect.bind(this);
    this.renderRedirect = this.renderRedirect.bind(this);
  }

  handleChangeTitle(e: React.FormEvent<HTMLInputElement>) {
    this.setState({title: e.currentTarget.value});
  }

  handleChangeContent(e: React.FormEvent<HTMLInputElement>) {
    this.setState({content: e.currentTarget.value});
  }

  setRedirect() {
    this.setState({
      redirect: true,
    });

    const data = {title: this.state.title, content: this.state.content};
    axios.post('http://localhost:4000/api/post', data).then(res => {
      console.log(res);
    });
  }

  renderRedirect = () => {
    if (this.state.redirect) {
      return <Redirect to="/post/finish" />;
    }
  };

  render() {
    return (
      <Container text style={{marginTop: '3em'}}>
        <Form onSubmit={this.setRedirect}>
          <Form.Input
            label="Title"
            name="title"
            value={this.state.title}
            onChange={this.handleChangeTitle}
          />
          <Form.Field
            label="Content"
            name="content"
            value={this.state.content}
            control="textarea"
            onChange={this.handleChangeContent}
          />
          {this.renderRedirect()}
          <Form.Button content="Submit" />
        </Form>
      </Container>
    );
  }
}
type Article struct {
    ID      int    `json:"id"`
    TITLE   string `json:"title"`
    CONTENT string `json:"content"`
}

var articles []Article

func main() {

    db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/article")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    router := gin.Default()

    api := router.Group("/api")
    {
        api.POST("/post", func(c *gin.Context) {
            var article Article
            c.BindJSON(&article)
            c.Header("Content-Type", "application/json")
            c.Header("Access-Control-Allow-Origin", "*")
            ins, err := db.Prepare("INSERT INTO articles(title,content) VALUES(?,?)")
            if err != nil {
                log.Fatal(err)
            }
            ins.Exec(article.TITLE, article.CONTENT)
            c.JSON(http.StatusOK, gin.H{"status": "ok"})
        })
    }
    router.Run(":4000")
}

I expect axios.post success to request, but actually failed with 404 status.

OPTIONS http://localhost:4000/api/post 404 (Not Found)
Access to XMLHttpRequest at 'http://localhost:4000/api/post' 
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.
createError.js:17 Uncaught (in promise) Error: Network Error
    at createError (createError.js:17)
    at XMLHttpRequest.handleError (xhr.js:80)
3
You should fix the CORS issue, it clearly doesn't help getting a right response code.prichrd
Your server must set the right CORS headers in order for the browser to make the POST request. For this, your server needs to respond to OPTIONS requests sent by the browser as well.Emile Bergeron
I already added c.Header("Access-Control-Allow-Origin", "*") in server side code. But still same error occurs.jpskgc
Your server only responds to POST requests, as seen with api.POST, but it should also respond to OPTIONS requests as well.Emile Bergeron
I got it. Issue was resolved. Thanks!!jpskgc

3 Answers

2
votes

Here is the working code that I tested:

type Article struct {
    ID      int    `json:"id"`
    TITLE   string `json:"title"`
    CONTENT string `json:"content"`
}

var articles []Article

func main() {

    db, err := sql.Open("mysql", "root:111111@tcp(localhost:3306)/article")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    router := gin.Default()

    router.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"*"},
        AllowMethods:     []string{"GET", "POST", "OPTIONS"},
        AllowHeaders:     []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        AllowOriginFunc: func(origin string) bool {
            return true
        },
        MaxAge: 15 * time.Second,
    }))
    api := router.Group("/api")
    {

        api.POST("/post", func(c *gin.Context) {
            var article Article
            c.BindJSON(&article)
            ins, err := db.Prepare("INSERT INTO articles(title,content) VALUES(?,?)")
            if err != nil {
                log.Fatal(err)
            }
            ins.Exec(article.TITLE, article.CONTENT)
            c.JSON(http.StatusOK, gin.H{"status": "ok"})
        })
    }
    router.Run(":4000")
}
0
votes

As the error says the request 'has been blocked by CORS policy', which is short for 'Cross-Origin Resource Sharing' and is a security measure implemented by the browser. The solution is to modify your server to return the correct 'Access-Control-Allow-Origin' header.

0
votes

After I added some codes in server side, issue was resolved.

        api.POST("/post", func(c *gin.Context) {
            c.Header("Content-Type", "application/json")
            c.Header("Access-Control-Allow-Origin", "*")
            // add 
            c.Header("Access-Control-Allow-Headers", "Content-Type")
            var article Article
            c.BindJSON(&article)
            ins, err := db.Prepare("INSERT INTO articles(title,content) VALUES(?,?)")
            if err != nil {
                log.Fatal(err)
            }
            ins.Exec(article.TITLE, article.CONTENT)
            c.JSON(http.StatusOK, gin.H{"status": "ok"})
        })
        // add response to OPTIONS
        api.OPTIONS("/post", func(c *gin.Context) {
            c.Header("Content-Type", "application/json")
            c.Header("Access-Control-Allow-Origin", "*")
            c.Header("Access-Control-Allow-Headers", "Content-Type")
            c.JSON(http.StatusOK, gin.H{"status": "ok"})
        })