【问题标题】:Enable CORS in Golang在 Golang 中启用 CORS
【发布时间】:2017-01-23 05:47:19
【问题描述】:

您好,我正在实施 rest api,为此我想允许跨源请求提供服务。

我目前在做什么:

AWS 上的 Go-server 代码:

func (c *UserController) Login(w http.ResponseWriter, r *http.Request, ctx *rack.Context) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
...
...
c.render.Json(w,rsp, http.StatusOK)
return
}

本地主机上的 Ajax 代码:

<script>
$( document ).ready(function() {
    console.log( "ready!" );
    $.ajax({
        url: 'http://ip:8080/login',
        crossDomain: true, //set as a cross domain requests
        withCredentials:false,
        type: 'post',
        success: function (data) {
            alert("Data " + data);
        },
    });
});

我在浏览器控制台上收到以下错误: XMLHttpRequest 无法加载 http://ip:8080/login。请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://localhost:8081' 不允许访问。响应的 HTTP 状态代码为 422。

我尝试添加预检选项:

func corsRoute(app *app.App) {
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization,X-CSRF-Token"

f := func(w http.ResponseWriter, r *http.Request) {
    if origin := r.Header.Get("Origin"); origin != "" {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
        w.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
        w.Header().Set("Access-Control-Expose-Headers", "Authorization")
    }
    return
}
app.Router.Options("/*p", f, publicRouteConstraint)
}

但它不起作用。

可以做些什么来解决它。

【问题讨论】:

  • 您在路由器中处理预检 OPTIONS 请求吗?
  • 不.. 需要吗?
  • 是的。看看这篇文章:stackoverflow.com/questions/22972066/…
  • 422 是Unprocessable Entity ,The request was well-formed but was unable to be followed due to semantic errors. 可能在这个方向找到?

标签: ajax go cors cross-domain


【解决方案1】:

为了允许CORS,您的服务器应在使用 OPTIONS 方法进行实际查询之前捕获浏览器发送到同一路径的所有Preflight request

第一种方法是通过以下方式手动管理:

func setupCORS(w *http.ResponseWriter, req *http.Request) {
    (*w).Header().Set("Access-Control-Allow-Origin", "*")
    (*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
    (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
    setupCORS(&w, req)
    if (*req).Method == "OPTIONS" {
        return
    }

    // process the request...
}

第二种方法是使用准备好的第三方pkg,例如https://github.com/rs/cors

package main

import (
    "net/http"

    "github.com/rs/cors"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte("{\"hello\": \"world\"}"))
    })

    // cors.AllowAll() setup the middleware with default options being
    // all origins accepted with simple methods (GET, POST). See
    // documentation below for more options.
    handler := cors.AllowAll().Handler(mux)
    http.ListenAndServe(":8080", handler)
}

【讨论】:

  • 是的,检查 OPTIONS 很重要,因为 CORS 检查使用 OPTIONS。如果 HTTP 请求 OPTIONS 失败,CORS 也会失败。
【解决方案2】:
  1. 第一:cors

    svc.Handle("/", restAPI.Serve(nil))
    
  2. 之后,我修复:Handle -> HandleFunc

    svc.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
         setupHeader(rw, req)
         if req.Method == "OPTIONS" {
             rw.WriteHeader(http.StatusOK)
             return
         }
         restAPI.Serve(nil).ServeHTTP(rw, req)
         return
     })
    
    
    
     func setupHeader(rw http.ResponseWriter, req *http.Request) {
            rw.Header().Set("Content-Type", "application/json")
            rw.Header().Set("Access-Control-Allow-Origin", "*")
            rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
            rw.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
    }
    

【讨论】:

    【解决方案3】:
    router := mux.NewRouter()
    api := router.PathPrefix("/api/v1").Subrouter()
    api.HandleFunc("/getLastDateOfAMonth", lastday.Handler).Methods(http.MethodPost)
    c := cors.New(cors.Options{
        AllowedOrigins:   []string{"http://localhost:3000"},
        AllowCredentials: true,
        AllowedMethods:   []string{"GET", "DELETE", "POST", "PUT"},
    })
    handler := c.Handler(router)
    log.Fatal(http.ListenAndServe(":3001", handler))
    

    请检查这个 -> https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request#:~:text=A%20CORS%20preflight%20request%20is,Headers%20%2C%20and%20the%20Origin%20header.

    我们都面临 CORS 问题 -> 修复 -> 后端服务器应该接受 CORS。将 cors 添加到您的后端应用程序。以便它了解来自浏览器的 Preflight 请求的 CORS。

    【讨论】:

      【解决方案4】:

      好的,这个问题给了我一些问题,但找到了一个你必须使用的修复

      github.com/gorilla/handlers
      

      连同 gollila/mux 库

      所以这是一个sn-p

      r := mux.NewRouter()
      header := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"})
      methods := handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"})
      origins := handlers.AllowedOrigins([]string{"*"})
      api := r.PathPrefix("/api").Subrouter()
      api.Handle("/route", function).Methods("GET", "OPTIONS")
      r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
          _, _ = fmt.Fprint(w, "hello")
      })
      
      err := http.ListenAndServe(":9000", handlers.CORS(header, methods, origins)(r))
      
      if err != nil {
          fmt.Println(err)
      }
      

      这应该可以解决你的问题

      【讨论】:

        【解决方案5】:

        除了所有出色的答案之外:您可能希望使用appHandler 模式,而不是在每个处理程序中设置标题:

        type Handler func(http.ResponseWriter, *http.Request) *Error
        
        func (fn Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
            if e := fn(w, r); e != nil { // e is *appError, not os.Error.
                http.Error(w, e.Message, e.Code)
            }
        }
        
        func Login(w http.ResponseWriter, r *http.Request) *Error {
           ...
           return nil
        }
        
        r.Handle("/login", Handler(Login))
        

        【讨论】:

          【解决方案6】:

          我使用gorilla/mux包构建Go RESTful API服务器,客户端使用JavaScript请求可以工作,

          我的 Go Server 运行在 localhost:9091,Server 代码:

          router := mux.NewRouter()
          //api route is /people, 
          //Methods("GET", "OPTIONS") means it support GET, OPTIONS
          router.HandleFunc("/people", GetPeopleAPI).Methods("GET", "OPTIONS")
          log.Fatal(http.ListenAndServe(":9091", router))
          

          我觉得这里给OPTIONS很重要,否则会报错:

          选项http://localhost:9091/people 405(不允许的方法)

          无法加载http://localhost:9091/people:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://localhost:9092' 不允许访问。响应的 HTTP 状态代码为 405。

          在允许OPTIONS 之后效果很好。我从This Article 得到这个想法。

          另外,MDN CORS doc 提及:

          此外,对于可能对服务器数据造成副作用的 HTTP 请求方法,规范要求浏览器 "preflight" 请求,使用 HTTP OPTIONS 从服务器请求支持的方法 请求方法,然后,在服务器“批准”后,使用实际的 HTTP 请求方法发送实际请求。

          以下是api GetPeopleAPI方法,注意在方法中我给评论//Allow CORS here By * or specific origin,我有另一个类似的答案解释这个概念CORS Here:

          func GetPeopleAPI(w http.ResponseWriter, r *http.Request) {
          
              //Allow CORS here By * or specific origin
              w.Header().Set("Access-Control-Allow-Origin", "*")
          
              w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
              // return "OKOK"
              json.NewEncoder(w).Encode("OKOK")
          }
          

          在客户端,我在localhost:9092上使用html和javascript,javascript会从localhost:9092向服务器发送请求

          function GetPeople() {
              try {
                  var xhttp = new XMLHttpRequest();
                  xhttp.open("GET", "http://localhost:9091/people", false);
                  xhttp.setRequestHeader("Content-type", "text/html");
                  xhttp.send();
                  var response = JSON.parse(xhttp.response);
                  alert(xhttp.response);
              } catch (error) {
                  alert(error.message);
              }
          }
          

          并且请求可以成功得到响应"OKOK"

          您也可以通过Fiddler等工具查看响应/请求头信息。

          【讨论】:

          • 添加多个 Access-Control-Allow-Headers 使用逗号分隔的列表,例如: w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Accept")
          • 这应该标记为最佳答案...非常有用...它解决了我的问题
          【解决方案7】:

          去服务器设置:

          package main
          
          import (
            "net/http"
          )
          
          
            func Cors(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Content-Type", "text/html; charset=ascii")
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.Header().Set("Access-Control-Allow-Headers","Content-Type,access-control-allow-origin, access-control-allow-headers")
                    w.Write([]byte("Hello, World!"))
            }
          
            func main() {
            mux := http.NewServeMux()
            mux.HandleFunc("/plm/cors",Cors)
            http.ListenAndServe(":8081", mux)
          }
          

          客户端 JQUERY AJAX 设置:

          <head>
                 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
                 </script>
          </head>
          <body>
          
                        <br> Please confirm to proceed : <button class="myConfirmButton1">Go!!</button>
          
                       <div id="loader1" style="display:none;">loading...</div>
                       <div id="loader2" style="display:none;">...done</div>
                       <div id="myFeedback1"></div>
          
                    <script>
                    $(document).ready(function(){
                      $(".myConfirmButton1").click(function(){
                        $('#loader1').show();
                        $.ajax({
                          url:"http://[webserver.domain.com:8081]/plm/cors",
                          dataType:'html',
                          headers: {"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "access-control-allow-origin, access-control-allow-headers"},
                          type:'get',
                          contentType: 'application/x-www-form-urlencoded',
                          success: function( data, textStatus, jQxhr ){
                          $('#loader1').hide();
                          $('#loader2').show();
                          $('#myFeedback1').html( data );
                                  },
                          error: function( jqXhr, textStatus, errorThrown ){
                          $('#loader1').hide();
                          $('#myFeedback1').html( errorThrown );
                          alert("error" + errorThrown);
                                  }
                          });
                     });
                    });
                    </script>
          </body>
          

          带有 curl 的客户端测试请求并获得响应:

          curl -iXGET http://[webserver.domain.com:8081]/plm/cors

          HTTP/1.1 200 OK
          Access-Control-Allow-Headers: Content-Type,access-control-allow-origin, access-control-allow-headers
          Access-Control-Allow-Origin: *
          Content-Type: text/html; charset=ascii
          Date: Wed, 17 Jan 2018 13:28:28 GMT
          Content-Length: 13
          
          Hello, World!
          

          【讨论】:

            【解决方案8】:

            感谢您提供的线索 - 都在标题中!我只在服务器端使用这些 golang 标头:

            w.Header().Set("Content-Type", "text/html; charset=utf-8")
            w.Header().Set("Access-Control-Allow-Origin", "*")
            

            现在可以使用这个 JQuery:

            <script 
            src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
            </script>
            <script>
            $.ajax({
                type: 'GET',
                url: 'https://www.XXXXXXX.org/QueryUserID?u=juXXXXny&p=blXXXXXne',
                crossDomain: true,
                dataType: 'text',
                success: function(responseData, textStatus, jqXHR) {
                    alert(responseData);
                        },
                error: function (responseData, textStatus, errorThrown) {
                    alert('POST failed.');
                }
            });
            </script>
            

            【讨论】:

            • 允许 CORS * 是个坏习惯
            • @V.Dalechyn 哦,是的 - 那么如果你有一个人们可以使用的公共 API,那么“正确”的方式是什么?
            • @alex 有没有人提到这条路线是公开的,可以从任何来源呼叫?如果作者试图在本地从浏览器发出 ajax 请求,他必须通过代理来欺骗他的浏览器。例如,CORS 必须只允许指定的来源,或者有人可以从网络钓鱼网站发布请求、检索 JWT 并继续进行提款
            • @V.Dalechyn 在这种情况下不是 - 但声明“这是不好的做法”并未涵盖所有用例。
            • @alex 为什么不在这种情况下? OP 正在实现登录控制器并在 . 内的 html 页面上使用 ajax 触发获取
            【解决方案9】:

            您可以查看https://github.com/rs/cors

            这也将处理 Options 请求

            【讨论】:

              猜你喜欢
              • 2021-12-05
              • 1970-01-01
              • 2020-02-18
              • 2013-06-12
              • 2013-01-06
              • 2020-02-18
              • 2015-06-15
              • 2019-01-01
              相关资源
              最近更新 更多