web开发-net-http

1. web应用简介

Web应用程序是一种可以直接通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件。

应用程序有两种模式C/S、B/S。C/S是客户端/服务器端程序,也就是说这类程序一般独立运行。而B/S就是浏览器端/服务器端应用程序,这类应用程序一般借助IE等浏览器来运行。WEB应用程序一般是B/S模式。Web应用程序首先是“应用程序”,和用标准的程序语言,如C、C++等编写出来的程序没有什么本质上的不同。然而Web应用程序又有自己独特的地方,就是它是基于Web的,而不是采用传统方法运行的。换句话说,它是典型的浏览器/服务器架构的产物。

一个Web应用程序是由完成特定任务的各种Web组件(web components)构成的并通过Web将服务展示给外界。在实际应用中,Web应用程序是由多个Servlet、JSP页面、HTML文件以及图像文件等组成。所有这些组件相互协调为用户提供一组完整的服务。

Web 应用在我们的生活中无处不在。看看我们日常使用的各个应用程序,它们要么是 Web 应用,要么是移动 App 这类 Web 应用的变种。无论哪一种编程语言,只要它能够开发出与人类交互的软件,它就必然会支持 Web 应用开发。对一门崭新的编程语言来说,它的开发者首先要做的一件事,就是构建与互联网( Internet)和万维网( World Wide Web)交互的库( library)和框架,而那些更为成熟的编程语言还会有各种五花八门的 Web 开发工具。

Go 是一门刚开始崭露头角的语言,它是为了让人们能够简单而高效地编写后端系统而创建的。这门语言拥有众多的先进特性,如函数式编程方面的特性、内置了对并发编程的支持、现代化的包管理系统、垃圾收集特性、以及一些包罗万象威力强大的标准库,而且如果需要我们还可以引入第三方开源库。

使用 Go 语言进行 Web 开发正变得日益流行,很多大公司都在使用,如 Google、Facebook、腾讯、百度、阿里巴巴、京东、小米以及 360、美团、滴滴以及新浪等。

2. web服务器的创建

Go 提供了一系列用于创建 Web 服务器的标准库,而且通过 Go 创建一个服务器的步骤非常简单,只要通过 net/http 包调用 ListenAndServe 函数并传入网络地址以及负责处理请求的处理器( handler )作为参数就可以了

http概述:

http是一个协议,通过get、put、post等方法跟web server进行交互

Get: 向特定的资源发出请求。如请求数据库中的数据,查询订单

Post:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。 向数据库插入数据,如提交订单,添加商品到购物车

Put: 向指定资源位置上传其最新内容。如修改用户名或者密码

Delete:请求服务器删除 Request-URI 所标识的资源。

3. net-http分析

Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现。

要使一个 http 服务器能够正常工作,必须至少实现如下的核心功能:

1、监听主机的某个端口

2、当在监听的端口上有客户端的请求到来时,接收该客户端的请求

3、处理客户端的请求

4. net-http实现

Go是在标准库中提供HTTP协议支持的系统语言,通过他可以快速简单的开发一个web服务器。同时,Go语言为开发者提供了很多便利。

4.1 net-http作为服务端

写一个最简单的server服务端,响应数据的,案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"fmt"
"net/http"
)

func main() {
//第一步:创建路由
http.HandleFunc("/get/hello", hello)
/*
"/get/hello "是路由,URL中除去域名的那一块
hello: 处理函数(处理服务请求)
*/

fmt.Println("http://127.0.0.1:8005/get/hello")
// 第三步:启动服务
http.ListenAndServe(":8005", nil)
/*
addr: 当前server监听的端口号和ip
handler:处理函数
nil:表示不绑定任何处理函数

*/
}

// 第二步:处理函数(处理get请求)
func hello(w http.ResponseWriter, r *http.Request) {
//响应数据,返回一个简单字符串
w.Write([]byte("hello world!"))
}

image-20240523215606871

为什么会这样?在Go语言里所有的可运行的包都必须命名为main。我们创建了main和hello两个函数。
  在main函数中,我们从net/http包中调用了一个http.HandleFucn函数来注册一个处理函数,在本例中是hello函数。这个函数接受两个参数。第一个是一个字符串,这个将进行路由匹配,在本例中是/get/hello路由。第二个函数是一个func (ResponseWriter, Request)的签名。我们的hello函数就是这样的签名。下一行中的http.ListenAndServe(":8005", nil),表示监听localhost8005端口,暂时忽略掉nil

   在hello函数中我们有两个参数,一个是http.ResponseWriter类型的。它类似响应流,实际上是一个接口类型。第二个是http.Request类型,类似于HTTP请求。我们不必使用所有的参数,就像再hello函数中一样。如果我们想返回“hello world”,那么我们只需要是用http.ResponseWriter

写一个server服务端,解析数据的,案例如下:

​ • http.HandleFunc(“/get/hello”, hello):注册一个路由,当访问/get/hello时,会调用hello函数处理请求。

​ • http.ListenAndServe(“:8005”, nil):启动HTTP服务器,监听8005端口,等待客户端请求。

​ • r.URL.Query():解析请求URL中的查询参数,返回一个map[string][]string,包含所有的查询参数。

​ • fmt.Println(query):将查询参数打印到服务器的控制台。

​ • w.Write([]byte(“hello world”)):向客户端写入”hello world”作为响应内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"net/http"
)

func main() {
//第一步:路由
//http://127.0.0.1:8005/get/hello?name=lucy
http.HandleFunc("/get/hello", hello)
http.ListenAndServe(":8005", nil)
}

func hello(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
fmt.Println(query)
w.Write([]byte("hello world"))
}

image-20240524132901629

image-20240524132917576

写一个server服务端,解析数据的,通过字典下标获取路由参数,案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"net/http"
)

func main() {
//第一步:路由
//http://127.0.0.1:8005/get/hello?name=lucy
http.HandleFunc("/get/hello", hello)
fmt.Println("http://127.0.0.1:8005/get/hello?name=lucy")
http.ListenAndServe(":8005", nil)
}

func hello(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
fmt.Println(query)
if len(query["name"]) > 0 {
names := query["name"][0]
fmt.Println("字典下标的值", names)
}
w.Write([]byte("hello world"))
}

image-20240524134040239

image-20240524134057307

server服务端,返回json数据,案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"encoding/json"
"fmt"
"net/http"
)

func main() {
//第一步:路由
//http://127.0.0.1:8005/get/hello?name=lucy
http.HandleFunc("/get/hello", hello)
fmt.Println("http://127.0.0.1:8005/get/hello?name=lucy")
http.ListenAndServe(":8005", nil)
}

func hello(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
fmt.Println(query)
if len(query["name"]) > 0 {
names := query["name"][0]
fmt.Println("字典下标的值", names)
}
name2 := query.Get("name")
fmt.Println("通过get方法实现:", name2)
//返回一个json数据
type Info struct {
Name string `json:"name"`
Password string `json:"password"`
Age int `json:"age"`
}

//假设这是从数据库中取出
u := Info{
Name: name2,
Password: "123456",
Age: 26,
}
json.NewEncoder(w).Encode(u)
w.Write([]byte("hello world"))
}

image-20240524135242148

image-20240524135256131

写一个简单的server服务端,处理post请求的,案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

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

func main() {
//第一步:路由
http.HandleFunc("/req/post", hello)
fmt.Println("http://127.0.0.1:8005/req/post")
// 第三步:启动服务
http.ListenAndServe(":8005", nil)
}

// 和get请求是一模一样的写法,这是一个post请求
func hello(w http.ResponseWriter, r *http.Request) {
// r *http.Request是结构的对象
// post请求从http的body中获取数据
// r.Body是结构体 Request 中的字段
//r.body 其实是*http.Request.body
bodyContent, _ := ioutil.ReadAll(r.Body) // 返回的是一个byte
fmt.Printf("%T %v", bodyContent, bodyContent)
// 如何才能解析这个string字符串(str转结构体)
w.Write([]byte("hello world Post"))
}

image-20240524141228695

image-20240524141236956

写一个client端,请求get数据,案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

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

/*
使用net/http模块,作为web的客户端发送get请求
应用场景:
1) 爬虫,获取页面数据
2) 调用其他服务中的接口
*/

func main() {

//1、直接通过url拼接出url字符串
apiUrl := "https://baidu.com"
resp, err := http.Get(apiUrl)
if err != nil {
fmt.Println(err)
}
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!