Go排坑:http.ServeMux意外重定向的问题分析

栏目: Go · 发布时间: 6年前

内容简介:Where is the原来go为了减少开发的重复性,简单的封装了一个默认的

http.ServeMux 是什么?官方定义为http服务的多路复用器。可以让开发在http服务器中自定义不同的path路由和对应的处理函数,我们简单举个例子:

package main 

import (
    "net/http"
    "fmt"
)

func HandleABCFunc(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%s %s\n", r.Method, r.URL)
} 

func main() { 
    http.HandleFunc("/abc/", HandleABCFunc) 
    http.ListenAndServe(":8080", nil) 
}
复制代码

Where is the http.ServeMux ? Are you kidding me? 别急,我们打开看看 http.HandleFunc 源码

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
...
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
复制代码

原来 go 为了减少开发的重复性,简单的封装了一个默认的 http.ServeMux ,也就是说通过 http.HandleFunc 注册的处理函数,统一由默认的 http.ServeMux 来解析和调用,如果你想定制 http.ServeMux 来处理自己的业务逻辑,那就需要修改上述例子:

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/abc/", HandleABCFunc)
	// ...
	http.ListenAndServe(":8080", mux)
}
复制代码

它有什么坑?

OK!既然知道什么是 http.ServeMux ,我这里说下最近使用它遇到的一个问题,我们以上面 /abc/ 为例子描述这个问题。

首先,把代码保存到server.go,直接使用go命令跑起来。

go run server.go
复制代码

然后,我们再编写一个客户端client.go,打印服务端的返回body体信息;接着与server.go一样,直接使用go命令跑起来。

package main

import (
	"net/http"
	"io/ioutil"
	"fmt"
)

func main() {
	resp, _ := http.Post("http://127.0.0.1:8080/abc", "", nil)
	if resp != nil {
		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		resp.Body.Close()
	}
}
复制代码

结果!我惊讶了!

go run client.go 
# GET /abc/
复制代码

我明明是POST请求,怎么服务端收到的是GET?难道我命中坑位?

似是而非

习惯性地,我用cURL请求调试一把服务端,发现了些端倪

curl -vL -XPOST http://127.0.0.1:8080/abc
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /abc HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Location: /abc/
< Date: Mon, 12 Nov 2018 02:49:30 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
< 
* Connection #0 to host 127.0.0.1 left intact
* Issue another request to this URL: 'http://127.0.0.1:8080/abc/'
* Found bundle for host 127.0.0.1: 0x55b8d54ce0c0 [can pipeline]
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /abc/ HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Mon, 12 Nov 2018 02:49:30 GMT
< Content-Length: 11
< Content-Type: text/plain; charset=utf-8
< 
POST /abc/
* Connection #0 to host 127.0.0.1 left intact
复制代码

如果直接看结果,cURL是预期的结果,还真以为是go的BUG,但仔细看了下请求过程,发现中间多了一次重定向请求,这就有点奇怪了?为什么go服务端会返回 301 Moved Permanently ?只好翻翻官网资料。

https://golang.org/pkg/net/http/#ServeMux

If a subtree has been registered and a request is received naming the subtree root without its
trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash).
This behavior can be overridden with a separate registration for the path without the trailing
slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images"
to "/images/", unless "/images" has been registered separately.
复制代码

对比上述例子,我请求的是 /abc 会被重定向为 /abc/ ,处理方式就是返回客户端让其自己重定向请求到 /abc/ ,听起来很合理,但go客户端为什么修改了我的请求method呢?难道是go标准库 http.Client 的BUG?再找找资料。

https://tools.ietf.org/html/rfc7231#section-6.4.2

Note: For historical reasons, a user agent MAY change the request
      method from POST to GET for the subsequent request.  If this
      behavior is undesired, the 307 (Temporary Redirect) status code
      can be used instead.
复制代码

真想大白!RFC7231中对 301 Moved Permanently 有一段额外说明,就是历史原因,客户端可能会将POST请求重定向为GET,为此如果真想不修改方法进行重定向,在HTTTP/1.1里面新定义了 307 Temporary Redirect 来实现。

回过头来看,cURL能执行正确,也说明了不同的客户端实现,会导致不同的效果。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

京东技术解密

京东技术解密

京东研发体系 / 电子工业出版社 / 2014-11-18 / 65

京东高速的增长、闪电响应的供应链、庞大的团队规模等背后内幕,对于业界一直像谜一样神秘。随着成为中国B2C领导厂商以及在纳斯达克上市,京东越来越需要开放自己,与业界形成更好的交流与融合。《京东技术解密》的面世,就是京东技术团队首次向业界集体亮相。本书用翔实的内容为读者逐一解答——如何用技术支撑网站的综合竞争实力,如何把握技术革新的时间点,如何应对各种棘手问题及压力,如何在网站高速运转的情况下进行系统......一起来看看 《京东技术解密》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具