首页 > 编程语言 >Golang HTTP编程及源码解析-请求/响应处理

Golang HTTP编程及源码解析-请求/响应处理

时间:2023-03-04 22:24:52浏览次数:53  
标签:Content HTTP 请求 Type Golang 源码 http 8000

1. HTTP协议

HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,基于TCP/IP通信协议来传递数据(HTML 文件、图片文件、查询结果等)。

  • HTTP 是无连接的:无连接的含义是限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接,采用这种方式可以节省传输时间。
  • HTTP是独立于媒体的:只要客户端和服务器都知道如何处理数据内容,任何类型的数据都可以通过HTTP发送。客户端和服务器都需要使用适当的 MIME 类型 指定内容类型。
  • HTTP是无状态的:HTTP 协议是无状态协议,无状态是指协议对于事务处理没有记忆能力,缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大,另一方面,在服务器不需要先前信息时它的应答就较快。

 

2. HTTP请求报文格式

HTTP请求报文包含以下三个部分。

  • 请求行
  • 请求头
  • 请求体

基本结构如下图所示

 

3. HTTP响应报文格式

与请求报文结构类型,HTTP响应报文包含以下三个部分

  • 状态行
  • 响应头
  • 响应体

基本结构如下图所示。

下面通过代码结合实例来看Golang的HTTP的请求处理和响应处理。

 

4. Golang HTTP请求处理

4.1 请求行

func requestLineHandler(w http.ResponseWriter, r *http.Request) {
    // 请求行
    fmt.Fprintf(w, "\nresp method:%v url:%v Proto:%v\n", r.Method, r.URL, r.Proto)
    // URL参数获取
    fmt.Fprintf(w, "resp URL查询参数:%v\n", r.URL.Query())
    w.WriteHeader(http.StatusOK)
}

func main() {
    // 1. 新建路由解码器
    h := http.NewServeMux()
    // 2. 路由注册
    h.HandleFunc("/reqline", requestLineHandler)
    // 3. 服务启动 阻塞监听
    http.ListenAndServe(":8000", h)
}

通过curl发起POST请求,通过-v打印请求报文和响应报文。

$ curl -v -X POST  http://localhost:8000/reqline?name=jack
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> POST /reqline?name=jack HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sat, 04 Mar 2023 12:13:25 GMT
< Content-Length: 94
< Content-Type: text/plain; charset=utf-8
<

resp method:POST url:/reqline?name=jack Proto:HTTP/1.1
resp URL查询参数:map[name:[jack]]

 

4.2 请求头

func requestHeaderHandler(w http.ResponseWriter, r *http.Request) {
    // 头部
    fmt.Fprintf(w, "header:\n")
    for key, val := range r.Header {
        fmt.Fprintf(w, "%v:%v\n", key, val)
    }
    fmt.Fprintf(w, "Get(\"Content-Type\"):%v\n", r.Header.Get("Content-Type"))
}
func main() {
    // 1. 新建路由解码器
    h := http.NewServeMux()
    // 2. 路由注册
    h.HandleFunc("/reqheader", requestHeaderHandler)
    // 3. 服务启动 阻塞监听
    http.ListenAndServe(":8000", h)
}

通过curl发起POST请求

可以看出curl的请求报文和HTTP服务端获取到的请求Header都包含version:1.1.1

$ curl -v -d "age=18" -H "Accept-Language:en-US" -H "version:1.1.1" http://localhost:8000/reqheader
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> POST /reqheader HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.86.0
> Accept: */*
> Accept-Language:en-US
> version:1.1.1
> Content-Length: 6
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sat, 04 Mar 2023 12:21:13 GMT
< Content-Length: 208
< Content-Type: text/plain; charset=utf-8
<
header:
Version:[1.1.1]
Content-Length:[6]
Content-Type:[application/x-www-form-urlencoded]
User-Agent:[curl/7.86.0]
Accept:[*/*]
Accept-Language:[en-US]
Get("Content-Type"):application/x-www-form-urlencoded

 

4.3 请求体

以下代码大致说明了Content-Typeapplication/x-www-form-urlencodedapplication/json的处理方法

func requestBodyHandler(w http.ResponseWriter, r *http.Request) {
    contentType := r.Header.Get("Content-Type")
    fmt.Fprintf(w, "Content-Type:%v\n", contentType)
    // URL参数获取
    fmt.Fprintf(w, "URL查询参数:%v\n", r.URL.Query())
    // 解析 Content-Type为multipart/form-data的请求体
    switch contentType {
    case "application/x-www-form-urlencoded": // 表单默认的提交数据的格式
        // 解析 URL查询参数 和 POST、PUT、PATCH的请求体参数
        // 并将结果放入r.Form, 且POST、PUT、PATCH的请求体参数的优先级比URL查询参数高
        // r.PostForm只存放POST、PUT、PATCH的请求体参数
        r.ParseForm()
        fmt.Fprintf(w, "PostForm:%v\n", r.PostForm)
        fmt.Fprintf(w, "Form:%v\n", r.Form)
    case "application/json": // json数据格式
        json, _ := ioutil.ReadAll(r.Body)
        fmt.Fprintf(w, "Json:%v", string(json))
    default:
        fmt.Fprintf(w, "unkown Content-Type")
    }
}

通过curl发起Content-Typeapplication/x-www-form-urlencodedPOST请求,

URL查询参数和请求体都包含name=,从Form:map[age:[18] name:[juli jack]]可以看出POST的请求体参数优先级更高。

$ curl -v http://localhost:8000/reqbody?name=jack -d "age=18" -d "name=juli"
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> POST /reqbody?name=jack HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.86.0
> Accept: */*
> Content-Length: 16
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sat, 04 Mar 2023 12:35:15 GMT
< Content-Length: 151
< Content-Type: text/plain; charset=utf-8
<
Content-Type:application/x-www-form-urlencoded
URL查询参数:map[name:[jack]]
PostForm:map[age:[18] name:[juli]]
Form:map[age:[18] name:[juli jack]]

通过curl发起Content-Typeapplication/json的请求。

$ curl -v http://localhost:8000/reqbody -H "Content-Type: application/json" -d "{"name":"jack","age":"18"}"
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> POST /reqbody HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.86.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 18
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sat, 04 Mar 2023 12:38:34 GMT
< Content-Length: 75
< Content-Type: text/plain; charset=utf-8
<
Content-Type:application/json
URL查询参数:map[]
Json:{name:jack,age:18}

 

5. Golang HTTP响应处理

func respHandler(w http.ResponseWriter, r *http.Request) {
	// 设置响应头
	w.Header().Set("name", "jack")
	// 设置cookie
	ck := &http.Cookie{
		Name:     "testCookie",
		Value:    "cookieval",
		Path:     "/",
		HttpOnly: true,
	}
	http.SetCookie(w, ck)
	// 设置响应体
	w.Write([]byte("hello i'm jack"))
	// 设置响应状态码
	w.WriteHeader(http.StatusOK)
}

func main() {
	// 1. 新建路由解码器
	h := http.NewServeMux()
	// 2. 路由注册
	h.HandleFunc("/resp", respHandler)
	// 3. 服务启动 阻塞监听
	http.ListenAndServe(":8000", h)
}

值得注意的是,设置Header、设置Cookie必须先于设置响应体,否则不会生效

$ curl -v http://localhost:8000/resp
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /resp HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Name: jack
< Set-Cookie: testCookie=cookieval; Path=/; HttpOnly
< Date: Sat, 04 Mar 2023 13:15:15 GMT
< Content-Length: 14
< Content-Type: text/plain; charset=utf-8
<
hello i'm jack

 

标签:Content,HTTP,请求,Type,Golang,源码,http,8000
From: https://www.cnblogs.com/amos01/p/17154558.html

相关文章

  • Http发展史
    HTTP是浏览器与服务端之间最主要的通信协议。20世纪60年代,美国国防部高等研究计划署(ARPA)建立了ARPA网,这被认为是互联网的起源。70年代,研究人员基于对ARPA网的实......
  • python-httpx 发送http2.0时代请求
    原文,自己做个笔记用https://blog.51cto.com/u_15127674/3872190官方文档的地址https://www.python-httpx.org/我们在日常开发中,经常会发送各种各样的网络请求。Python......
  • 【代理】【六】代理源码解析-Cglib代理-代理文件执行分析
    1 前言上节我们简单看了Enhancer的generateClass和 firstInstance,两个方法的内容也就是创建代理以及生成代理对象的内容,这节我们结合实际生成的文件了解一下代理......
  • HTTP请求和响应格式说明
    前置知识HTTP特点HTTP协议是基于TCP/IP协议的应用层传输协议,即规范数据传输的一种规则。当客户端和服务器建立TCP连接后,发送HTTP请求和接收HTTP响应都是通过访问Socket......
  • docker-compose up -d 运行报错: ERROR: https://dl-cdn.alpinelinux.org/alpine/v3.1
    docker部署python时执行到RUNapkupdate....命令出现错误:ERROR:https://dl-cdn.alpinelinux.org/alpine/v3.17/community:BADsignature一般是软件包源地址请求......
  • 源码编译 - hadoop3.31
    hadoop源码编译一、准备工作1、下载hadoop源码官网地址:https://www.apache.org/index.html#projects-list清华大学镜像网站:https://mirrors.tuna.tsinghua.edu.cn/apa......
  • 重新编译harbor-exporter源码,构建镜像
    1.githubhttps://github.com/c4po/harbor_exporter 2.dockerfileFROMgolang:1.17ENVGO111MODULE=on\GOPROXY="https://goproxy.cn,direct"COPYharbor_exp......
  • 萌新也能看懂的 Golang 题解(一)
    写在前面关于“模拟题”和“算法题”及主观难度评价第一批1791.设备编号(模拟)1792.服务器集群网络延时(排序、数学)1793.给定差值的组合(哈希表)1787.最长元音子串(模......
  • 萌新也能看懂的 Golang 题解(二)
    第二批1807.矩阵转置(数学)难度:简单;主观评价:简单。简单模拟题+数学题(判断完全平方数)。先判断矩阵长度是否为完全平方数(开根号然后自身相乘,判断和开根号之前的数是否一致......
  • 萌新也能看懂的 Golang 题解(三)
    第三批1822.电话拦截(模拟、排序)难度:中等;主观评价:简单。sort.Slice() 应用题,重点在于通配符的判断和如何设计数据结构保证最后能按呼叫顺序返回通话记录。对于没有通......