-
[Golang] gin-gonic CORS 문제 해결방법언어/Golang 2017. 3. 17. 16:57
먼저 CORS에 대한 개념이 어느정도 존재한다는 가정하에 진행합니다.
TEST 환경
자바스크립트 -> Go Gin-gonic API를 요청하는 상황입니다.
Request Headers
var req = new XMLHttpRequest(); req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); req.withCredentials = true; req.setRequestHeader("Authorization", "Bearer "+$.cookie("AUTH_A_TOKEN"));
Response Headers
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin") c.Header("Access-Control-Allow-Credentials", "true") c.Header("Access-Control-Allow-Origin", "http://foo.com:8080") c.Header("Access-Control-Allow-Methods", "GET, DELETE, POST")
주의사항
위 Response Headers의 환경은 OPTIONS에만 나열하면 절대 안됩니다. OPTIONS에만 작성하고 GET, POST 등 메인 메소드에 작성하지 않을 경우 CORS문제가 발생했다고 에러가 계속나오게 됩니다. (몇 시간헤맴...)
자세하게 Simple Request의 경우에는 서버에서 요청을 1번만 하고 요청에 대한 응답을 1번만 하게되어 OPTIONS 메서드를 호출하지 않습니다. (즉, GET을 요청했을 경우 GET에 대한 요청과 응답이 1번씩만 출력됩니다.) 하지만 Simple Request가 아닌 모든 경우에는 Preflight Request로 요청이 되는데 이때는 예비요청, 예비응답, 본 요청, 본 응답 으로 요청 2번, 응답 2번을 하게 됩니다. 예비 요청은 서버에서 구현하지 않아도 자동적으로 OPTIONS 메서드를 호출하게되고 해당 응답이 정상일 경우 본 요청을 하게 됩니다. (즉, GET을 요청하면 먼저 OPTIONS 요청, OPTIONS 응답 한 다음 정상적인 응답일 경우에 GET 요청, GET 응답이 됩니다.) 따라서 OPTIONS와 본 요청 메서드에 response header환경을 전부 적용해야 합니다.
해결법
위 header옵션들을 사용할 메소드마다 전부 나열해줍니다.
func main() { router = gin.Default() router.GET("/test", func(c *gin.Context) { c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin") c.Header("Access-Control-Allow-Credentials", "true") c.Header("Access-Control-Allow-Origin", "http://foo.com:8080") c.Header("Access-Control-Allow-Methods", "GET, DELETE, POST") c.Next() if http.StatusOK == 200 { ... c.JSON(200, gin.H{ "status": http.StatusOK, "message": "ㅁ", "data": nil, }) } }) router.POST("/test", func(c *gin.Context) { c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin") c.Header("Access-Control-Allow-Credentials", "true") c.Header("Access-Control-Allow-Origin", "http://foo.com:8080") c.Header("Access-Control-Allow-Methods", "GET, DELETE, POST") c.Next() if http.StatusOK == 200 { ... c.JSON(200, gin.H{ "message":"success", "status":http.StatusOK, "data":nil, }) } }) router.OPTIONS("/test", func(c *gin.Context) { c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin") c.Header("Access-Control-Allow-Credentials", "true") c.Header("Access-Control-Allow-Origin", "http://foo.com:8080") c.Header("Access-Control-Allow-Methods", "GET, DELETE, POST") c.AbortWithStatus(204) }) router.Run(":8081") }
위 코드는 잘 동작하지만 중복되는 코드가 계속해서 포함됩니다. 아래와 같이 깔끔하게 변경할 수 있습니다.
func main() { router = gin.Default() router.Use(CORSMiddleware()) router.GET("/test", func(c *gin.Context) { if http.StatusOK == 200 { ... c.JSON(200, gin.H{ "status": http.StatusOK, "message": "ㅁ", "data": nil, }) } }) router.POST("/test", func(c *gin.Context) { if http.StatusOK == 200 { ... c.JSON(200, gin.H{ "message":"success", "status":http.StatusOK, "data":nil, }) } }) router.Run(":8081") } func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin") c.Header("Access-Control-Allow-Credentials", "true") c.Header("Access-Control-Allow-Origin", "http://foo.com:3000") c.Header("Access-Control-Allow-Methods", "GET, DELETE, POST") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204) return } c.Next() } }