การตั้งค่าส่วนหัว HTTP


165

ฉันกำลังพยายามตั้งส่วนหัวในเว็บเซิร์ฟเวอร์ Go ของฉัน ฉันใช้gorilla/muxและnet/httpแพ็คเกจ

ฉันต้องการตั้งค่าAccess-Control-Allow-Origin: *ให้อนุญาตข้ามโดเมน AJAX

นี่คือรหัส Go ของฉัน:

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)
}

net/httpแพคเกจมีเอกสารที่อธิบายการส่งส่วนหัวของคำขอ http ราวกับว่ามันเป็นไคลเอนต์ - ฉันไม่แน่ใจว่าวิธีการที่ส่วนหัวของการตอบสนองชุด?

คำตอบ:


227

ไม่เป็นไรฉันคิดออก - ฉันใช้Set()วิธีการในHeader() (doh!)

ตัวจัดการของฉันดูเหมือนตอนนี้:

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

บางทีนี่อาจช่วยใครบางคนที่คาเฟอีนถูกกีดกันเช่นเดียวกับฉัน :)


2
ฉันเคยมีปัญหาเดียวกันมันอาจจะเป็นประโยชน์ในการเพิ่ม:w.Header().Add("Access-Control-Allow-Methods", "PUT") w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
เรย์

1
สิ่งนี้จะไม่ทำงานในกรณีที่ชุด AJAX ไคลเอนต์withCredentials:true(ค่า "*" ไม่ได้รับอนุญาตเมื่อส่งข้อมูลรับรองซึ่งเป็นกรณีการใช้งานทั่วไป) คุณต้องตั้งค่าเริ่มต้นเป็นผู้ร้องขอ (ดูคำตอบของ Matt Bucci ด้านล่างเพื่อดูว่า)
orcaman

98

คำตอบทั้งหมดข้างต้นไม่ถูกต้องเนื่องจากไม่สามารถจัดการคำขอ preflight OPTIONS ได้วิธีแก้ปัญหาคือแทนที่ส่วนต่อประสานของเราเตอร์ mux ดูAngularJS $ http ได้รับการร้องขอล้มเหลวด้วยส่วนหัวที่กำหนดเอง (alllowed ใน 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
"ทั้งหมดข้างต้น" ... สามารถเรียงคำตอบได้หลายวิธีดังนั้นวลีนี้ไม่ได้หมายถึงสิ่งที่คุณต้องการ
Dave C

คำขอ CORS แบบง่าย ๆ ไม่มีการ preflight ทุกอย่างขึ้นอยู่กับสิ่งที่คุณพยายามให้บริการ
laike9m

อย่าลืมAccess-Control-Allow-Credentials": "true"คำขอด้วย httpOnly Cookies
Federico

23

อย่าใช้ '*' เพื่อจุดเริ่มต้นจนกว่าคุณจะต้องการพฤติกรรมสาธารณะโดยสมบูรณ์
ตามที่Wikipedia พูดว่า :

"ค่าของ" * "เป็นสิ่งพิเศษที่ไม่อนุญาตให้มีการร้องขอข้อมูลประจำตัวหมายถึงการตรวจสอบความถูกต้อง HTTP, ใบรับรอง SSL ฝั่งไคลเอ็นต์และไม่อนุญาตให้ส่งคุกกี้"

ซึ่งหมายความว่าคุณจะได้รับข้อผิดพลาดจำนวนมากโดยเฉพาะใน Chrome เมื่อคุณพยายามที่จะใช้งานตัวอย่างเช่นการตรวจสอบสิทธิ์อย่างง่าย

นี่คือเสื้อคลุมที่ถูกแก้ไข:

// 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)
    }
}

และอย่าลืมตอบกลับส่วนหัวเหล่านี้ทั้งหมดไปยังคำขอ preflight OPTIONS


1
ฉันไม่ค่อยเข้าใจการใช้ตัวคลุมกระดาษนี้คุณสามารถยกตัวอย่างวิธีห่อหุ้มตัวจัดการ http ด้วยรหัสนี้ได้หรือไม่? ฉันใช้กอริลลา mux ดังนั้นการใช้งานปัจจุบันของฉันคือ router.HandleFunc("/user/action", user.UserAction) http.Handle("/", router) http.ListenAndServe(":8080", nil).Set("Access-Control-Allow-Origin", "*")
Matt Bucci

2
ตอนนี้ฉันกำลังตัดสายเรียกเข้าของฉันด้วย addDefaultHeaders เหมือน router.HandleFunc("/user/action", addDefaultHeaders(user.UserAction)) แต่ฉันมีประมาณ 16 เส้นทางนี้ไม่เหมาะมีวิธีใดที่จะระบุว่าเป็น wrapper ที่แพคเกจ http หรือเลเยอร์เราเตอร์ mux
Matt Bucci

14

ตั้งค่ามิดเดิ้ล golang ที่เหมาะสมเพื่อให้คุณสามารถนำไปใช้กับอุปกรณ์ปลายทางได้

ประเภทผู้ช่วยและฟังก์ชั่น

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
}

มิดเดิลแวร์จริง

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)
        })
    }
}

ปลายทาง

จำ! มิดเดิ้ลแวร์นำไปใช้กับการสั่งซื้อย้อนกลับ (ExpectGET () ได้รับไฟก่อน)

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

14

หากคุณไม่ต้องการแทนที่เราเตอร์ของคุณ (หากคุณไม่ได้กำหนดค่าแอพของคุณในลักษณะที่รองรับสิ่งนี้หรือต้องการกำหนดค่า CORS บนเส้นทางตามเส้นทางพื้นฐาน) เพิ่มตัวจัดการ OPTIONS เพื่อจัดการคำขอเที่ยวบินล่วงหน้า .

เช่นด้วย Gorilla Mux เส้นทางของคุณจะมีลักษณะดังนี้:

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

หมายเหตุข้างต้นว่านอกเหนือจากการจัดการโพสต์ของเราที่เรากำลังกำหนดวิธีการจัดการตัวเลือกที่เฉพาะเจาะจง

จากนั้นเพื่อจัดการกับวิธี preflight ตัวเลือกจริงคุณสามารถกำหนด AccountsCreatePreFlight ดังนี้:

// 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")
}

สิ่งที่ทำให้คลิกทั้งหมดนี้สำหรับฉัน (นอกเหนือจากการทำความเข้าใจวิธีการทำงานของ CORS) คือวิธี HTTP ของคำขอ preflight นั้นแตกต่างจากวิธี HTTP ของคำขอจริง ในการเริ่มต้น CORS เบราว์เซอร์จะส่งคำขอ preflight ด้วย HTTP Method OPTIONS ซึ่งคุณต้องจัดการอย่างชัดเจนในเราเตอร์ของคุณและจากนั้นหากได้รับการตอบสนองที่เหมาะสม"Access-Control-Allow-Origin": origin (หรือ "*" สำหรับทุกคน) จากแอปพลิเคชันของคุณ ขอร้อง

ฉันเชื่อว่าคุณสามารถทำได้เพียง "*" สำหรับประเภทคำขอมาตรฐาน (เช่น: GET) แต่สำหรับคนอื่นคุณจะต้องตั้งค่าต้นกำเนิดอย่างชัดเจนเช่นเดียวกับข้างต้น


12

ฉันสร้างเสื้อคลุมสำหรับกรณีนี้:

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

ฉันมีปัญหาเดียวกันกับที่อธิบายไว้ข้างต้นวิธีแก้ปัญหาที่ระบุด้านบนถูกต้องการตั้งค่าที่ฉันมีดังต่อไปนี้ 1) Angularjs สำหรับลูกค้า 2) เฟรมเวิร์ก Beego สำหรับเซิร์ฟเวอร์ GO

โปรดปฏิบัติตามประเด็นเหล่านี้ 1) การตั้งค่า CORS จะต้องเปิดใช้งานเฉพาะบนเซิร์ฟเวอร์ GO 2) อย่าเพิ่มส่วนหัวใด ๆ ใน angularJS ยกเว้นสิ่งนี้

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

ในเซิร์ฟเวอร์ GO ของคุณเพิ่มการตั้งค่า CORS ก่อนที่คำขอจะเริ่มดำเนินการเพื่อให้คำขอ preflight ได้รับ 200 OK หลังจากนั้นวิธี OPTIONS จะถูกแปลงเป็น GET, POST, PUT หรือประเภทคำขอของคุณ


-7

ฉันรู้ว่านี่เป็นคำตอบที่ต่างออกไป แต่นี่ไม่ใช่เรื่องที่น่ากังวลสำหรับเว็บเซิร์ฟเวอร์หรือ ตัวอย่างเช่นnginxสามารถช่วยได้

ngx_http_headers_moduleโมดูลช่วยเพิ่ม“หมดอายุ” และ“Cache-Control” ฟิลด์ส่วนหัวและสาขาโดยพลการไปยังส่วนหัวการตอบสนอง

...

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

การเพิ่มnginxต่อหน้าบริการ Go ของคุณในการผลิตดูเหมือนจะฉลาด มันมีคุณสมบัติอีกมากมายสำหรับการให้สิทธิ์การบันทึกและการปรับเปลี่ยนคำขอ นอกจากนี้ยังช่วยให้สามารถควบคุมผู้ที่สามารถเข้าถึงบริการของคุณและไม่เพียง แต่สามารถระบุพฤติกรรมที่แตกต่างกันสำหรับสถานที่เฉพาะในแอปของคุณดังที่แสดงไว้ด้านบน

ฉันสามารถอธิบายเกี่ยวกับสาเหตุที่ต้องใช้เว็บเซิร์ฟเวอร์กับ go api ของคุณ แต่ฉันคิดว่านั่นเป็นหัวข้อสำหรับการสนทนาอื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.