Đặt tiêu đề HTTP


165

Tôi đang cố gắng đặt tiêu đề trong máy chủ web Go của mình. Tôi đang sử dụng gorilla/muxnet/httpgói.

Tôi muốn đặt Access-Control-Allow-Origin: *để cho phép AJAX tên miền chéo.

Đây là mã Go của tôi:

func saveHandler(w http.ResponseWriter, r *http.Request) {
// do some stuff with the request data
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/save", saveHandler)
    http.Handle("/", r)
    http.ListenAndServe(":"+port, nil)
}

Các net/httpgói có tài liệu mô tả gửi tiêu đề yêu cầu http như thể nó là một khách hàng - Tôi không chắc chắn chính xác làm thế nào để tiêu đề phản ứng bộ?

Câu trả lời:


227

Không sao, tôi đã hiểu ra rồi - Tôi đã sử dụng Set()phương pháp trên Header()(doh!)

Xử lý của tôi trông như thế này bây giờ:

func saveHandler(w http.ResponseWriter, r *http.Request) {
    // allow cross domain AJAX requests
    w.Header().Set("Access-Control-Allow-Origin", "*")
}

Có lẽ điều này sẽ giúp một người nào đó bị thiếu caffeine như tôi đôi khi :)


2
Tôi đã có cùng một vấn đề, nó cũng có thể hữu ích để thêm: w.Header().Add("Access-Control-Allow-Methods", "PUT") w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
Ray

1
Điều này sẽ không hoạt động trong trường hợp máy khách AJAX thiết lập withCredentials:true(giá trị "*" không được phép khi thông tin đăng nhập được gửi, đây là trường hợp sử dụng phổ biến). Bạn phải đặt nguồn gốc cho người yêu cầu (xem câu trả lời của Matt Bucci bên dưới để biết cách thực hiện).
orcaman

98

Tất cả các câu trả lời ở trên đều sai vì chúng không thể xử lý yêu cầu ánh sáng TÙY CHỌN, giải pháp là ghi đè lên giao diện của bộ định tuyến mux. Xem AngularJS $ http nhận yêu cầu không thành công với tiêu đề tùy chỉnh (được nhắc đến trong CORS)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/save", saveHandler)
    http.Handle("/", &MyServer{r})
    http.ListenAndServe(":8080", nil);

}

type MyServer struct {
    r *mux.Router
}

func (s *MyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    if origin := req.Header.Get("Origin"); origin != "" {
        rw.Header().Set("Access-Control-Allow-Origin", 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")
    }
    // Stop here if its Preflighted OPTIONS request
    if req.Method == "OPTIONS" {
        return
    }
    // Lets Gorilla work
    s.r.ServeHTTP(rw, req)
}

19
"Tất cả các câu trả lời trên" có thể được sắp xếp theo nhiều cách để cụm từ này không có nghĩa là bạn muốn nó.
Dave C

Các yêu cầu CORS đơn giản không có ánh sáng trước, tất cả phụ thuộc vào những gì bạn đang cố gắng phục vụ.
laike9m

Đừng quên Access-Control-Allow-Credentials": "true"yêu cầu với httpOnly Cookies.
Federico

23

Không sử dụng '*' cho Xuất xứ, cho đến khi Bạn thực sự cần một hành vi hoàn toàn công khai.
Như Wikipedia nói :

"Giá trị của" * "đặc biệt ở chỗ nó không cho phép các yêu cầu cung cấp thông tin xác thực, nghĩa là xác thực HTTP, chứng chỉ SSL phía máy khách và cũng không cho phép gửi cookie."

Điều đó có nghĩa là, bạn sẽ gặp rất nhiều lỗi, đặc biệt là trong Chrome khi bạn cố gắng thực hiện ví dụ như một xác thực đơn giản.

Đây là một trình bao bọc chính xác:

// Code has not been tested.
func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if origin := r.Header.Get("Origin"); origin != "" {
            w.Header().Set("Access-Control-Allow-Origin", origin)
        }
        w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
        w.Header().Set("Access-Control-Allow-Credentials", "true")
        fn(w, r)
    }
}

Và đừng quên trả lời tất cả các tiêu đề này cho yêu cầu TÙY CHỌN trước.


1
Tôi hoàn toàn không hiểu cách sử dụng trình bao bọc này, bạn có thể đưa ra một ví dụ về cách bạn sẽ xử lý http của mình bằng mã này không? Tôi đang sử dụng khỉ đột nên sử dụng hiện tại của tôi là router.HandleFunc("/user/action", user.UserAction) http.Handle("/", router) http.ListenAndServe(":8080", nil).Set("Access-Control-Allow-Origin", "*")
Matt Bucci

2
Bây giờ tôi đang kết thúc các cuộc gọi xử lý của mình với addDefaultHeaders như router.HandleFunc("/user/action", addDefaultHeaders(user.UserAction)) tuy nhiên vì tôi có khoảng 16 tuyến đường, điều này không lý tưởng là có cách nào để chỉ định nó là một trình bao bọc trong gói http hoặc lớp bộ định tuyến mux
Matt Bucci

14

Đặt một phần mềm trung gian golang thích hợp, để bạn có thể sử dụng lại trên bất kỳ điểm cuối nào.

Loại trợ giúp và chức năng

type Adapter func(http.Handler) http.Handler
// Adapt h with all specified adapters.
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
    for _, adapter := range adapters {
        h = adapter(h)
    }
    return h
}

Phần mềm trung gian thực tế

func EnableCORS() Adapter {
    return func(h http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

            if origin := r.Header.Get("Origin"); origin != "" {
                w.Header().Set("Access-Control-Allow-Origin", 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")
            }
            // Stop here if its Preflighted OPTIONS request
            if r.Method == "OPTIONS" {
                return
            }
            h.ServeHTTP(w, r)
        })
    }
}

Điểm cuối

NHỚ! Middleware được áp dụng theo thứ tự ngược lại (ExpectGET () được kích hoạt trước)

mux.Handle("/watcher/{action}/{device}",Adapt(api.SerialHandler(mux),
    api.EnableCORS(),
    api.ExpectGET(),
))

14

Nếu bạn không muốn ghi đè bộ định tuyến của mình (nếu bạn không cấu hình ứng dụng của mình theo cách hỗ trợ điều này hoặc muốn định cấu hình CORS trên tuyến theo cơ sở tuyến đường), hãy thêm trình xử lý TÙY CHỌN để xử lý yêu cầu chuyến bay trước .

Tức là, với Gorilla Mux, các tuyến đường của bạn sẽ như sau:

accounts := router.Path("/accounts").Subrouter()
accounts.Methods("POST").Handler(AccountsCreate)
accounts.Methods("OPTIONS").Handler(AccountsCreatePreFlight)

Lưu ý ở trên rằng ngoài trình xử lý POST của chúng tôi, chúng tôi đang xác định một trình xử lý phương thức TÙY CHỌN cụ thể .

Và sau đó để thực sự xử lý phương thức preflight TÙY CHỌN, bạn có thể định nghĩa AccountCreatePreFlight như sau:

// Check the origin is valid.
origin := r.Header.Get("Origin")
validOrigin, err := validateOrigin(origin)
if err != nil {
    return err
}

// If it is, allow CORS.
if validOrigin {
    w.Header().Set("Access-Control-Allow-Origin", origin)
    w.Header().Set("Access-Control-Allow-Methods", "POST")
    w.Header().Set("Access-Control-Allow-Headers",
        "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

Điều thực sự làm cho tất cả điều này nhấp vào tôi (ngoài việc thực sự hiểu cách thức hoạt động của CORS) là Phương thức HTTP của yêu cầu preflight khác với Phương thức HTTP của yêu cầu thực tế. Để khởi tạo CORS, trình duyệt gửi yêu cầu preflight với CHỌN Phương thức HTTP, mà bạn phải xử lý rõ ràng trong bộ định tuyến của mình và sau đó, nếu nó nhận được phản hồi thích hợp "Access-Control-Allow-Origin": origin(hoặc "*" cho tất cả) từ ứng dụng của bạn, nó sẽ khởi tạo thực tế yêu cầu.

Tôi cũng tin rằng bạn chỉ có thể thực hiện "*" cho các loại yêu cầu tiêu chuẩn (ví dụ: GET), nhưng đối với những người khác, bạn sẽ phải đặt rõ ràng nguồn gốc như tôi làm ở trên.


12

Tôi tạo trình bao bọc cho trường hợp này:

func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        fn(w, r)
    }
}

1

Tôi đã có cùng một vấn đề như được mô tả ở trên, các giải pháp được đưa ra ở trên là chính xác, thiết lập tôi có như sau 1) Angularjs cho Máy khách 2) Khung Beego cho máy chủ GO

Vui lòng làm theo các điểm sau 1) Chỉ cài đặt CORS trên máy chủ GO 2) KHÔNG thêm bất kỳ loại tiêu đề nào trong angularJS ngoại trừ điều này

.config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];
    }])

Trong máy chủ GO của bạn, hãy thêm cài đặt CORS trước khi yêu cầu bắt đầu được xử lý để yêu cầu preflight nhận được 200 OK sau đó phương thức TÙY CHỌN sẽ được chuyển đổi thành GET, POST, PUT hoặc loại yêu cầu của bạn.


-7

Tôi biết đây là một thay đổi khác nhau trong câu trả lời, nhưng đây không phải là mối quan tâm nhiều hơn cho một máy chủ web? Ví dụ, nginx , có thể giúp đỡ.

Các ngx_http_headers_module mô-đun cho phép thêm “Hết hạn” và “Cache-Control” lĩnh vực tiêu đề, và các lĩnh vực tùy ý, đến một phản ứng tiêu đề

...

location ~ ^<REGXP MATCHING CORS ROUTES> {
    add_header Access-Control-Allow-Methods POST
    ...
}
...

Thêm nginx trước dịch vụ đi của bạn trong sản xuất có vẻ khôn ngoan. Nó cung cấp nhiều tính năng hơn để ủy quyền, ghi nhật ký và sửa đổi các yêu cầu. Ngoài ra, nó còn cung cấp khả năng kiểm soát ai có quyền truy cập vào dịch vụ của bạn và không chỉ vậy mà người ta có thể chỉ định hành vi khác nhau cho các vị trí cụ thể trong ứng dụng của bạn, như đã trình bày ở trên.

Tôi có thể tiếp tục về lý do tại sao sử dụng máy chủ web với api go của bạn, nhưng tôi nghĩ đó là một chủ đề cho một cuộc thảo luận khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.