You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang服务跨域报错:请求资源缺失Access-Control-Allow-Origin头

跨域预检请求失败问题排查与解决

问题描述

从Angular应用向Go后端发送HTTP请求时,收到报错:

预检请求未通过跨域检查:请求资源上不存在'Access-Control-Allow-Origin'头。

已手动添加响应头,但问题仍未解决。

相关代码

server.go

package rest

import (
    "context"
    "fmt"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "syscall"

    "github.com/gorilla/mux"
    "github.com/randyVerduguez/randy-verduguez_06122023-BE-challenge/configs"
    "github.com/randyVerduguez/randy-verduguez_06122023-BE-challenge/http/rest/handlers"
    "github.com/randyVerduguez/randy-verduguez_06122023-BE-challenge/pkg/db"
    "github.com/rs/cors"
    "github.com/sirupsen/logrus"
)

type Server struct {
    logger *logrus.Logger
    router *mux.Router
    config configs.Config
}

func NewServer() (*Server, error) {
    config, err := configs.NewParsedConfig()

    if err != nil {
        return nil, err
    }

    database, err := db.Connect(db.ConfigDB{
        Host:     config.Database.Host,
        Port:     config.Database.Port,
        User:     config.Database.User,
        Password: config.Database.Password,
        Name:     config.Database.Name,
    })

    if err != nil {
        return nil, err
    }

    log, err := NewLogger()

    if err != nil {
        return nil, err
    }

    router := mux.NewRouter()

    handlers.Register(router, log, database)

    server := Server{
        logger: log,
        config: config,
        router: router,
    }

    return &server, nil
}

func (s *Server) Run(ctx context.Context) error {
    cors := cors.New(cors.Options{
        AllowedMethods: []string{"GET, POST"},
        AllowedOrigins: []string{"http://localhost:4200"},
        AllowedHeaders: []string{"Content-Type", "Accept"},
    })

    server := http.Server{
        Addr:    fmt.Sprintf(":%d", s.config.ServerPort),
        Handler: cors.Handler(s.router),
    }

    stopServer := make(chan os.Signal, 1)

    signal.Notify(stopServer, syscall.SIGINT, syscall.SIGTERM)

    defer signal.Stop(stopServer)

    serverErrors := make(chan error, 1)

    var wg sync.WaitGroup
    wg.Add(1)

    go func(wg *sync.WaitGroup) {
        defer wg.Done()
        s.logger.Printf("REST API listening on  %d", s.config.ServerPort)
        serverErrors <- server.ListenAndServe()
    }(&wg)

    select {
    case err := <-serverErrors:
        return fmt.Errorf("error: starting REST API server %w", err)
    case <-stopServer:
        s.logger.Warn("server recieved STOP signal")

        err := server.Shutdown(ctx)

        if err != nil {
            return fmt.Errorf("graceful shutdown did not complete: %w", err)
        }

        wg.Wait()
        s.logger.Info("server was shutdown gracefully")
    }

    return nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "http://localhost:4200")
    w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
    w.Header().Set("Access-Control-Allow-Credentials", "true")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Accept, origin, Cache-Control, X-Requested-With")

    if r.Method == "OPTIONS" {
        return
    }

    s.router.ServeHTTP(w, r)
}

routes.go

package handlers

import (
    "net/http"

    "github.com/gorilla/mux"
    "github.com/jmoiron/sqlx"
    "github.com/sirupsen/logrus"
)

func Register(r *mux.Router, lg *logrus.Logger, db *sqlx.DB) {
    handler := newHandler(lg, db)

    r.Use(handler.MiddlewareLogger())
    r.HandleFunc("/weather/current", handler.GetCurrentWeather()).Methods(http.MethodPost)
    r.HandleFunc("/weather/welcome", handler.Test()).Methods(http.MethodGet)
}

问题根源与解决方法

你当前代码同时使用了两种CORS处理方式:github.com/rs/cors库和自定义ServeHTTP方法,两者冲突导致CORS头处理混乱;同时rs/cors的配置存在语法错误。

  1. 修正rs/cors的AllowedMethods配置
    当前写法[]string{"GET, POST"}是错误的,每个HTTP方法需作为单独字符串元素,且必须包含预检请求用的OPTIONS方法:

    AllowedMethods: []string{"GET", "POST", "OPTIONS"},
    
  2. 移除自定义的ServeHTTP方法
    既然已经用rs/cors库处理CORS,无需再手动编写ServeHTTP设置响应头,两者共存会导致头信息冲突,无法正确处理预检请求。

  3. 补全必要的CORS配置项
    将你自定义ServeHTTP中设置的允许头、凭证支持等配置迁移到rs/cors的选项中,确保覆盖所有需求。

修正后的Run方法中CORS配置部分:

func (s *Server) Run(ctx context.Context) error {
    cors := cors.New(cors.Options{
        AllowedMethods:   []string{"GET", "POST", "OPTIONS"},
        AllowedOrigins:   []string{"http://localhost:4200"},
        AllowedHeaders:   []string{"Content-Type", "Accept", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "origin", "Cache-Control", "X-Requested-With"},
        AllowCredentials: true,
    })

    server := http.Server{
        Addr:    fmt.Sprintf(":%d", s.config.ServerPort),
        Handler: cors.Handler(s.router),
    }
    // 其余代码保持不变
}

删除整个ServeHTTP方法后,rs/cors库就能正确处理所有CORS相关请求,包括预检OPTIONS请求,不会再出现缺少Access-Control-Allow-Origin头的问题。

内容的提问来源于stack exchange,提问作者Papilo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:25:55