319 lines
7.3 KiB
Markdown
319 lines
7.3 KiB
Markdown
# 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>
|
||
```
|