2

I have a backend REST API service written in Golang. I used axios in the React frontend to POST to the API. Even though I think I have enabled the CORS for both the frontend and backend, the browser still throws this error:

Access to XMLHttpRequest at 'http://localhost:8080/winERC20' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.

Can anyone please suggest what I should do to solve this?

main.go

func main() {
    fmt.Println("Server is serving at http://localhost:8080/")
    // Init the mux router
    router := mux.NewRouter()
    router.HandleFunc("/", helloHandler)
    router.HandleFunc("/matchingABI", api.MatchingContractABI)
    router.HandleFunc("/winERC20", api.WinERC20_controller).Methods("POST", "OPTIONS")
    log.Fatal(http.ListenAndServe(":8080", router))
}

api.go

    func WinERC20_controller(w http.ResponseWriter, r *http.Request) {
        enableCors(&w)

        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        // Try to decode the request body into the struct. If there is an error,
        // respond to the client with the error message and a 400 status code.
        var p winERC20_RequestBody
        err := json.NewDecoder(r.Body).Decode(&p)
        if err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }
    
        ...
    
        w.Header().Set("Content-Type", "application/json")
        resp := make(map[string]string)
        resp["message"] = "Success"
        jsonResp, err := json.Marshal(resp)
        if err != nil {
            log.Fatalf("Error happened in JSON marshal. Err: %s", err)
        }
        w.Write(jsonResp)
    }
    
    func enableCors(w *http.ResponseWriter) {
        header := (*w).Header()
        header.Add("Access-Control-Allow-Origin", "*")
        header.Add("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS")
        header.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
    }

frontend.js

grantERC20(){
        // Transfer ERC20 to the player
        let url = 'http://localhost:8080/winERC20'
        let config = {
            headers: {
                "Content-Type": "application/json",
                'Access-Control-Allow-Origin': '*',
            }
        }
        let data = {
            "PublicAddress" : this.props.account,
            "Amount": this.props.score
        }
        axios.post(url, data, config)
        .then(
            (response) => {console.log(response)},
            (error) => {console.log(error);}
        );
    }

componentDidMount () {
        this.grantERC20()
}
0

4 Answers 4

1

I found the cause of the error in the following way. First I put a breakpoint in the function where the uadmin backend processed the request and followed the call stack back by putting several breakpoints, then I repeated the request again and followed the execution of the code function by function and this is what I found:

1- Before doing several things golang calls the following function:

// Handler returns the handler to use for the given request,
// consulting r.Method, r.Host, and r.URL.Path. It always returns
// a non-nil handler. If the path is not in its canonical form, the
// handler will be an internally-generated handler that redirects
// to the canonical path. If the host contains a port, it is ignored
// when matching handlers.
//
// The path and host are used unchanged for CONNECT requests.
//
// Handler also returns the registered pattern that matches the
// request or, in the case of internally-generated redirects,
// the pattern that will match after following the redirect.
//
// If there is no registered handler that applies to the request,
// Handler returns a “page not found” handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

    // CONNECT requests are not canonicalized.
    if r.Method == "CONNECT" {
        // If r.URL.Path is /tree and its handler is not registered,
        // the /tree -> /tree/ redirect applies to CONNECT requests
        // but the path canonicalization does not.
        if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
            return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
        }

        return mux.handler(r.Host, r.URL.Path)
    }

    // All other requests have any port stripped and path cleaned
    // before passing to mux.handler.
    host := stripHostPort(r.Host)
    path := cleanPath(r.URL.Path)

    // If the given path is /tree and its handler is not registered,
    // redirect for /tree/.
    if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
        return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    }

    if path != r.URL.Path {
        _, pattern = mux.handler(host, path)
        u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
        return RedirectHandler(u.String(), StatusMovedPermanently), pattern
    }

    return mux.handler(host, r.URL.Path)
}

What I most want to highlight here is the following line:

// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
    return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}

By analyzing it carefully it is possible to understand that the urls used to call the APIs must end with the character '/'.

So if you use the http POST method of the axios library and pass it a url without '/' at the end, then the browser first executes OPTIONS to receive the cross-origin headers (CORS) and what golang does is redirect the request. Then the browser shows you a message similar to the following in the console:

Cross-origin request blocked: The same origin policy does not allow reading the remote resource at http://127.0.0.1:8080/login. (Reason: External redirection of CORS request is not allowed.)

This is even if you specify "proxy": "http://127.0.0.1:8080" in package.json or even if you use some extension to enable CORS.

This is by using the call to the axios post method as follows:

await axios.post('http://127.0.0.1:8080/login', {username: 'admin', password: 'admin'}, {headers: {'Content-Type': 'application/json'}})

result of incorrect url when connecting with golang

However, the correct way to make the POST request so that it does not redirect to the OPTIONS request that comes before the POST is by putting the '/' character at the end of the url:

await axios.post('http://127.0.0.1:8080/login/', {username: 'admin', password: 'admin'}, {headers: {'Content-Type': 'application/json'}}) 

And in fact I have found that it also works even if you do not use an extension that enables cross origin or even if you do not set cors headers in the request.

Sign up to request clarification or add additional context in comments.

Comments

0

Why, in your client-side code, are you adding a header named Access-Control-Allow-Origin to your request? That header is a response header, not a request header. Moreover, CORS preflight is bound to fail because your CORS configuration doesn't allow such a request header:

header.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")

The remedy is simple: just drop that Access-Control-Allow-Origin header from your request.

Besides, instead of implementing CORS "manually" (which is error-prone), you should consider relying on some proven CORS middleware, such as https://github.com/rs/cors or a more modern alternative like https://github.com/jub0bs/cors.

Comments

-1

You should add the cors inside your server like this

package routes

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
)

func Setup(app *fiber.App) {
    app.Use(cors.New())
}

it's all i need to solve my issue

Comments

-3

TL;DR: Use Fetch instead of Axios in the React Frontend and add the backend API URL (http://localhost:8080) as proxy in package.json.

I faced the same issue, and after spending a day into it, trying each and every solution available, I came to a fix by replacing the Axios call with fetch.

In Frontend.js:

 await fetch("http://localhost:8080/winERC20", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams(data),
    });

In Package.json:

"proxy":"http://localhost:8080",

Edit: I had installed the CORS extension

3 Comments

Simply using fetch rather than Axios could not have solved the OP's problem, though.
There are some known issues with Axios. Sometimes it does not work correctly, especially when there's a CORS issue. Using Fetch can be one of the alternatives.
@Avik i'd hazard a guess 100% of the issues you ran into with axios were due to your usage of axios, rather than axios itself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.