背景

网上找的有坑,调查半天,原来是中间件实现的不对,特此分享一下,希望对其他人有帮助。

实现

中间件

//MiddlewareCors 设置跨域请求头

func MiddlewareCors() middleware.Middleware {

return func(handler middleware.Handler) middleware.Handler {

return func(ctx context.Context, req interface{}) (interface{}, error) {

if ts, ok := transport.FromServerContext(ctx); ok {

if ht, ok := ts.(http.Transporter); ok {

ht.ReplyHeader().Set("Access-Control-Allow-Origin", "*")

ht.ReplyHeader().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")

ht.ReplyHeader().Set("Access-Control-Allow-Credentials", "true")

ht.ReplyHeader().Set("Access-Control-Allow-Headers", "Content-Type,"+

"X-Requested-With,Access-Control-Allow-Credentials,User-Agent,Content-Length,Authorization")

}

}

return handler(ctx, req)

}

}

}

使用

// NewHTTPServer new a HTTP server.

func NewHTTPServer(c *conf.Server, timeline *service.TimelineService, logger log.Logger) *http.Server {

var opts = []http.ServerOption{

http.Middleware(

recovery.Recovery(),

logging.Server(logger),

MiddlewareCors(),

),

}

if c.Http.Network != "" {

opts = append(opts, http.Network(c.Http.Network))

}

if c.Http.Addr != "" {

opts = append(opts, http.Address(c.Http.Addr))

}

if c.Http.Timeout != nil {

opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))

}

srv := http.NewServer(opts...)

v1.RegisterTimelineHTTPServer(srv, timeline)

return srv

}

前端

前端啥也不用设置,正常使用即可。

下面的代码是前后端分离的,用go启动了一个静态html服务(端口是3000),本地服务的端口是8000,所以是跨域的。

访问接口通过:

具体原理,可以参考这篇文章:

4种方法解决js跨域的实现方式

404跨域

实测中,如果前端接口写错,仍然会报跨域错误。看了下 kratos 的 http.NewServer()的源码:

// NewServer creates an HTTP server by options.

func NewServer(opts ...ServerOption) *Server {

srv := &Server{

network: "tcp",

address: ":0",

timeout: 1 * time.Second,

middleware: matcher.New(),

dec: DefaultRequestDecoder,

enc: DefaultResponseEncoder,

ene: DefaultErrorEncoder,

strictSlash: true,

}

for _, o := range opts {

o(srv)

}

srv.router = mux.NewRouter().StrictSlash(srv.strictSlash)

srv.router.NotFoundHandler = http.DefaultServeMux

srv.router.MethodNotAllowedHandler = http.DefaultServeMux

srv.router.Use(srv.filter())

srv.Server = &http.Server{

Handler: FilterChain(srv.filters...)(srv.router),

TLSConfig: srv.tlsConf,

}

return srv

}

我们发现针对404的默认处理没有支持跨域,仍然是用 runtime net/http 包的默认处理函数。

新增 NotFoundHandler :

func NotFoundHandler(res nethttp.ResponseWriter, req *nethttp.Request) {

res.Header().Set("Access-Control-Allow-Origin", "*")

res.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")

res.Header().Set("Access-Control-Allow-Credentials", "true")

res.Header().Set("Access-Control-Allow-Headers", "Content-Type,"+

"X-Requested-With,Access-Control-Allow-Credentials,User-Agent,Content-Length,Authorization")

log.Info("NotFoundHandler")

errCode := errors.NotFound("page not found", "page not found")

buffer, _ := json.Marshal(errCode)

//res.WriteHeader(400)

_, _ = res.Write(buffer)

}

使用:

// NewHTTPServer new a HTTP server.

func NewHTTPServer(c *conf.Server, timeline *service.TimelineService, logger log.Logger) *http.Server {

// 这里,注册not found路由

nethttp.HandleFunc("/", NotFoundHandler)

var opts = []http.ServerOption{

http.Middleware(

recovery.Recovery(),

logging.Server(logger),

MiddlewareCors(),

),

}

// ...

}

nethttp包会使用默认的 Mux:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {

DefaultServeMux.HandleFunc(pattern, handler)

}

也就是kratos中 srv.router.NotFoundHandler = http.DefaultServeMux 指定的Mux。

Reference:

implementing_a_custom_404_error_messagean-introduction-to-handlers-and-servemuxes-in-gogolang-dissecting-listen-and-serve

好文阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: