Files
notes/resource/go/待总结/015 Go 语言 http 编程.md
2026-03-01 01:43:46 +08:00

319 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Web 基础架构
## 1. Web 类型
- **静态 Web**: 所有用户看到的内容完全相同
- **动态 Web**: 根据用户身份、权限等展示不同内容
## 2. Web 应用架构
- **B/S (Browser/Server)**: 基于浏览器的 Web 应用
- **C/S (Client/Server)**: 需要安装的客户端程序
- 优势: 跨平台支持 (Windows/Mac/Linux)
## 3. 网络协议
- **HTTP**:
- 标准端口: 80
- 用途: 传输超文本(文本、图片、视频等)
- **HTTPS**:
- 标准端口: 443
- 特点: 加密传输,更安全
## 4. 通信模型
**请求-响应模式**:
- 请求 (Request):
- 来源: 客户端(浏览器)
- 内容: URL 地址(如 https://www.baidu.com)
- 方式: GET、POST、PUT、DELETE 等
- 响应 (Response):
- 来源: 服务端
- 功能: 处理请求并返回数据
- 主要操作: CRUD(增删改查),占比约 80%
## 5. 工作流程
1. 客户端通过 TCP/IP 连接服务器
2. 发送 HTTP(S) 请求:
- 普通请求: www.example.com/login?username=xxx&pwd=xxx
- 文件上传: upload/video?file=xxx
3. 服务器处理请求
4. 返回响应结果
5. 客户端展示结果
6. 断开连接
## 开发调试
- 使用浏览器开发者工具 (F12 或右键检查)
- 分析请求/响应数据
- 优化代码性能和健壮性
# helloworld
go 语言中所有和网络相关的都在 net 包下,http 也在 net 包下。
包含了客户端和服务端的代码实现。
未来我们学习的所有框架,都是基于这些底层代码的。
请求(request- 响应(response
```go
package main
import (
"fmt"
"net/http"
)
// 一个简单的服务端代码实现
// http程序启动之后是不会停止的,一直监听请求
func main() {
// 写一些请求来接收浏览器的信息
// HandleFunc http请求的处理函数
// func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
// localhost:8080/hello url -> 代码处理
http.HandleFunc("/hello", hello)
// 有一个地址给浏览器访问,什么都没有。404
// func ListenAndServe(addr string, handler Handler)
// localhost 本机(127.0.0.1 端口 8080
// nil默认处理器,空的 404
// 开启监听程序的代码是放在main方法的最后一行的。
http.ListenAndServe("localhost:8080", nil)
}
// 请求和响应
func hello(resp http.ResponseWriter, req *http.Request) {
// 查看一些请求信息 (/login:用户名和密码来匹配登录 /user/id 接收用户的id然后查询用户信息 )
fmt.Println(req.URL)
fmt.Println(req.Method)
fmt.Println(req.RemoteAddr)
//...
// 一般会响应一些信息给客户端 (文字、网页) resp.Write
// 响应一段文字[]byte("hello,web")
// 响应一段html代码 []byte("html代码") 网页
resp.Write([]byte("<h1 style=\"color: red;\">hello,web</h1>"))
}
```
代码来写客户端
```go
package main
import (
"fmt"
"io"
"net/http"
)
// 手写客户端访问
func main() {
/*
http.Get()
*/
// 请求方式 请求的url 接收响应结果
resp, _ := http.Get("http://localhost:8080/hello")
// 通过defer关闭连接 resp.Body 响应的主体
defer resp.Body.Close()
fmt.Println(resp.Body)
fmt.Println(resp.Status) // 200 OK
fmt.Println(resp.Header) // 响应头
// 接收具体的响应内容
buf := make([]byte, 1024)
for {
n, err := resp.Body.Read(buf)
if err != nil && err != io.EOF {
fmt.Println("读取出现了错误")
return
} else {
fmt.Println("读取完毕")
res := string(buf[:n])
fmt.Println("服务器响应的数据为:", res)
break
}
}
}
```
# 带参数的请求
客户端编写
- url的参数拼接 ?拼接 & 连接
```go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// 复杂请求
urlStr := "http://127.0.0.1:8080/login" // ?
// 参数如何拼接到url上,参数封装为数据url.Values{}
data := url.Values{}
data.Set("username", "admin") // ?
data.Set("password", "123456") // ?
// 将url字符串转化为url对象,并给携带数据
urlNew, _ := url.ParseRequestURI(urlStr)
urlNew.RawQuery = data.Encode()
// http://127.0.0.1:8080/login?password=123456&username=kuangshen
// ? get的传参,多个参数之间使用 & 连接
fmt.Println(urlNew)
// 发请求,参数是一个地址
resp, _ := http.Get(urlNew.String())
defer resp.Body.Close()
// 读取resp信息,返回buf
buf, _ := io.ReadAll(resp.Body)
fmt.Println(string(buf))
}
```
后台处理代码
```go
func login(resp http.ResponseWriter, req *http.Request) {
// 模拟数据库中存在一个数据
mysqlUserData := "admin"
mysqlPwdData := "123456"
fmt.Println("接收到了login请求")
// 拿到请求中的参数
urlData := req.URL.Query()
username := urlData.Get("username")
password := urlData.Get("password")
// 登录逻辑, 将客户端发送的数据和系统数据比对实现登录业务
if username == mysqlUserData {
if password == mysqlPwdData {
resp.Write([]byte("登录成功"))
} else {
resp.Write([]byte("密码错误"))
}
} else {
resp.Write([]byte("登录失败"))
}
}
```
# 表单参数获取
html表单
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<!--表单-->
<!--http://localhost:8080/register?username=kuangshen&password=123456-->
<form action="http://localhost:8080/register" method="post">
<h2>注册</h2>
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<input type="submit" value="注册">
</form>
</body>
</html>
```
后端处理代码
```go
func register(resp http.ResponseWriter, req *http.Request) {
fmt.Println("接收到了注册请求")
// 处理表单的请求, 前端提交表单-后盾解析表单
req.ParseForm() // 解析表单
// 获取表单参数 post
username := req.PostForm.Get("username")
password := req.PostForm.Get("password")
fmt.Println(username, password)
// 很多的判断
// 短信
// 验证码
resp.Write([]byte("注册成功"))
}
```
# 要给前端响应数据 (了解即可)
- 我们给前端数据
- 页面模板要渲染数据!有很多语法,看看就好。类似于Java中的JSP 、或者PHP里面的代码
- 代码+页面融合,很乱,已经被这个时代抛弃了
- 前后端分离(我们后面都用这种方式)
1、通过代码跳转到页面 template
```go
// 通过请求,进入页面(路由) temp.Execute(resp, data)
// 通过URl进入某个页面
func findAll(resp http.ResponseWriter, req *http.Request) {
// 接收到前端的信息 /findAll, 查询全部用户
userMap := make(map[int]User)
userMap[1] = User{"KUANGSHEN", 1}
userMap[2] = User{"xiaoming", 2}
// 返回给前端页面并渲染上去
temp, _ := template.ParseFiles("./userlist.html")
data := make(map[string](map[int]User))
data["data"] = userMap
temp.Execute(resp, data)
}
```
2、前端的一些简答语法(了解即可,和php、jsp、C#差不多
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>查询用户</title>
</head>
<body>
<!--获取后端的数据 {{.data}}
遍历
{{range $k,$v := .data}}
{{end}}
-->
{{range $k,$v := .data}}
{{$k}}
{{if eq $k 1}}
{{.Name}}
{{$v}}
{{end}}
{{end}}
</body>
</html>
```