Reputation: 695
I have implemented go-swagger for my API documentation which is running on a different port on my localhost and my application is running on the port 8888. I have implemented cors https://github.com/rs/cors
my code for implementing cors is
var Router = func() *mux.Router{
router := mux.NewRouter()
var c = cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowCredentials: true,
AllowedMethods :[]string{"POST", "PUT","GET","DELETE","OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
MaxAge: 300,
// Enable Debugging for testing, consider disabling in production
Debug: true,
})
RegisterHandler := http.HandlerFunc(controllers.Register)
router.Handle("/api/register",c.Handler(middleware.RequestValidator(RegisterHandler,reflect.TypeOf(dto.UserRequest{})))).Methods("POST")
fmt.Println("var1 = ", reflect.TypeOf(router))
return router
}
When hitting the request from Postman it seems the code is running fine
Postman Response Header
access-control-allow-credentials →true
access-control-allow-origin →*
content-length →123
content-type →application/json
date →Wed, 14 Oct 2020 04:02:37 GMT
vary →Origin
As I have enabled debug while implementing cors middleware log printed on my console is as follows
Console log
[cors] 2020/10/14 09:32:37 Handler: Actual request
[cors] 2020/10/14 09:32:37 Actual response added headers: map[Access-Control-Allow-Credentials:[true] Access-Control-Allow-Origin:[*] Vary:[Origin]]
Issue
When I accessing the same API from Swagger-UI in the browser I am getting cors issue that the "Access-Control-Allow-Origin" header is not set
Access to fetch at 'http://localhost:8888/api/register' from origin 'http://localhost:45601' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
And there is no log printed on the console.
It seems while accessing the API from Swagger UI, cors middleware code is not accessible.
Here is the bowser network call detail of the response for swagger
HTTP METHOD = OPTIONS
General
Request URL: http://localhost:8888/api/register
Request Method: OPTIONS
Status Code: 405 Method Not Allowed
Remote Address: [::1]:8888
Referrer Policy: strict-origin-when-cross-origin
Response Header
Content-Length: 0
Date: Wed, 14 Oct 2020 04:25:23 GMT
Request Header
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en-IN;q=0.9,en;q=0.8
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Cache-Control: no-cache
Connection: keep-alive
Host: localhost:8888
Origin: http://localhost:45601
Pragma: no-cache
Referer: http://localhost:45601/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
Fetch
General
Request URL: http://localhost:8888/api/register
Referrer Policy: strict-origin-when-cross-origin
Request Header
Provisional headers are shown
accept: application/json
Content-Type: application/json
Referer: http://localhost:45601/
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
Thank you!
Upvotes: 1
Views: 4535
Reputation: 1
This is annoying when I work to try to enable CORS and write header in Go. Finally, I create a struct wrapping ResponseWriter to detect header is already written or not and it work fine.
package router
import (
"log"
"net/http"
)
const (
noWritten = -1
defaultStatus = http.StatusOK
)
type ResponseWriter struct {
writer http.ResponseWriter
size int
status int
}
func (w *ResponseWriter) Writer() http.ResponseWriter {
return w.writer
}
func (w *ResponseWriter) WriteHeader(code int) {
if code > 0 && w.status != code {
if w.Written() {
log.Printf("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code)
}
w.status = code
}
}
func (w *ResponseWriter) WriteHeaderNow() {
if !w.Written() {
w.size = 0
w.writer.WriteHeader(w.status)
}
}
func (w *ResponseWriter) Write(data []byte) (n int, err error) {
w.WriteHeaderNow()
n, err = w.writer.Write(data)
w.size += n
return
}
func (w *ResponseWriter) Status() int {
return w.status
}
func (w *ResponseWriter) Size() int {
return w.size
}
func (w *ResponseWriter) Written() bool {
return w.size != noWritten
}
And in the response:
func respondJSON(w *router.ResponseWriter, status int, payload interface{}) {
res, err := json.Marshal(payload)
if err != nil {
respondError(w, internalErrorStatus.number, internalErrorStatus.description)
return
}
go w.WriteHeader(status)
header := w.Writer().Header()
header.Add("Access-Control-Allow-Origin", "*")
header.Add("Content-Type", "application/json")
header.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
header.Add("Access-Control-Allow-Headers", "*")
w.Write([]byte(res))
}
Upvotes: 0
Reputation: 71
You need to allow OPTIONS
method on the router.
https://github.com/abhimanyu1990/go-connect/blob/main/app/conf/router.configuration.go#L30
router.Handle("/api/register", c.Handler(middleware.RequestValidator(RegisterHandler, reflect.TypeOf(dto.UserRequest{})))).Methods("POST", "OPTIONS")
Upvotes: 1