web开发
1. 跨域
跨域是浏览器基于
同源策略
的一种安全手段。
同源:
- 协议相同(
protocol
) - 主机相同(
host
) - 端口相同(
port
)
当访问
不是同源
的服务地址时,就会发生跨域问题
1.1 go-zero跨域配置
第一种
server := rest.MustNewServer(c.RestConf, rest.WithCors("http://localhost:5173"))
1
2第二种
- 在有些情况下,我们需要传递一些自定义的
header
,这时候仍旧会出现跨域问题 - 所以我们需要自定义跨域的header
server := rest.MustNewServer(c.RestConf, rest.WithCorsHeaders("xxxx"), )
1
2
3- 在有些情况下,我们需要传递一些自定义的
第三种
- 如果我们既想要自定义header,又想要控制跨域的域名
server := rest.MustNewServer(c.RestConf, //顺序不能颠倒 rest.WithCors("http://localhost:5173"), rest.WithCorsHeaders("xxxx"), )
1
2
3
4
5第四种
如果想要自定义跨域
server := rest.MustNewServer(c.RestConf, rest.WithCustomCors(func(header http.Header) { var allowOrigin = "Access-Control-Allow-Origin" var allOrigins = "http://localhost:5173" var allowMethods = "Access-Control-Allow-Methods" var allowHeaders = "Access-Control-Allow-Headers" var exposeHeaders = "Access-Control-Expose-Headers" var methods = "GET, HEAD, POST, PATCH, PUT, DELETE, OPTIONS" var allowHeadersVal = "xxxx, Content-Type, Origin, X-CSRF-Token, Authorization, AccessToken, Token, Range" var exposeHeadersVal = "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers" var maxAgeHeader = "Access-Control-Max-Age" var maxAgeHeaderVal = "86400" header.Set(allowOrigin, allOrigins) header.Set(allowMethods, methods) header.Set(allowHeaders, allowHeadersVal) header.Set(exposeHeaders, exposeHeadersVal) header.Set(maxAgeHeader, maxAgeHeaderVal) }, func(w http.ResponseWriter) { }), )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2. 请求参数
2.1 form表单
type Request struct {
Name string `form:"name"` // 必填参数
Age int `form:"age,optional"` // optional定义非必填参数
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2.2 json
type Request struct {
Name string `json:"name"`
Age int `json:"age"`
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
7
2
3
4
5
6
7
2.3 path
type Request struct {
Name string `path:"name"`
}
// Path定义
rest.Route{
Method: http.MethodGet,
Path: "/user/:name",
Handler: handle,
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
2.4 header参数
type Request struct {
Authorization string `header:"authorization"`
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
2
3
4
5
6
2.5 参数默认值
type Request struct {
Age int `form:"age,default=18"`
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
2
3
4
5
6
2.6 参数枚举值
type Request struct {
Age int `form:"age,options=18|19"`
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
2
3
4
5
6
2.7 参数区间定义
type Request struct {
Age int `form:"age,range=[18:35)"`
}
var req Request
err := httpx.Parse(r, &req) // 解析参数
1
2
3
4
5
6
2
3
4
5
6
3. 中间件
3.1 内置中间件
- 鉴权管理中间件 AuthorizeHandler
- 熔断中间件 BreakerHandler
- 内容安全中间件 ContentSecurityHandler
- 解密中间件 CryptionHandler
- 压缩管理中间件 GunzipHandler
- 日志中间件 LogHandler
- ContentLength 管理中间件 MaxBytesHandler
- 限流中间件 MaxConnsHandler
- 指标统计中间件 MetricHandler
- 普罗米修斯指标中间件 PrometheusHandler
- panic 恢复中间件 RecoverHandler
- 负载监控中间件 SheddingHandler
- 超时中间件 TimeoutHandler
- 链路追踪中间件 TraceHandler
type MiddlewaresConf struct {
Trace bool `json:",default=true"`
Log bool `json:",default=true"`
Prometheus bool `json:",default=true"`
MaxConns bool `json:",default=true"`
Breaker bool `json:",default=true"`
Shedding bool `json:",default=true"`
Timeout bool `json:",default=true"`
Recover bool `json:",default=true"`
Metrics bool `json:",default=true"`
MaxBytes bool `json:",default=true"`
Gunzip bool `json:",default=true"`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Name: HelloWorld.api
Host: 127.0.0.1
Port: 8080
Middlewares:
Metrics: false
1
2
3
4
5
2
3
4
5
3.2 自定义中间件
server := rest.MustNewServer(rest.RestConf{})
defer server.Stop()
server.Use(middleware)
// 自定义的中间件
func middleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-Middleware", "static-middleware")
next(w, r)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
4. redis集成
4.1 docker-compose部署配置
version: '3.3'
services:
redis:
container_name: zero-redis-node
image: redis:latest
restart: always
ports:
- "6379:6379"
volumes:
- ./volume/redis/dаta:/root/redis
environment:
- REDIS_PASSWORD=mszlu
- REDIS_PORT=6379
- REDIS_DATABASES=16
redis-cluster-master:
image: redis:latest
container_name: redis-cluster-master
restart: always
command: redis-server --port 7000 --requirepass mszlu --appendonly yes
ports:
- 7000:7000
volumes:
- ./volume/redis-cluster/data:/data
networks:
- redis-cluster
redis-cluster-slave1:
image: redis:latest
container_name: redis-slave-1
restart: always
command: redis-server --slaveof redis-cluster-master 7000 --port 7001 --requirepass mszlu --masterauth mszlu --appendonly yes
ports:
- 7001:7001
depends_on:
- master
volumes:
- ./volume/redis-cluster/data:/data
networks:
- redis-cluster
networks:
redis-cluster:
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
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
4.2 单节点
go-zero内置配置:
RedisConf struct {
Host string
Type string `json:",default=node,options=node|cluster"`
Pass string `json:",optional"`
Tls bool `json:",optional"`
NonBlock bool `json:",default=true"`
// PingTimeout is the timeout for ping redis.
PingTimeout time.Duration `json:",default=1s"`
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- Host:Redis 服务地址
ip:port
, 如果是 Redis Cluster 则为ip1:port1,ip2:port2,ip3:port3
- Type:node 单节点 Redis ,cluster Redis 集群
- Pass:认证密码
- Tls:是否开启tls
- NonBlock:是否以非阻塞模式启动
- PingTimeout:ping超时时间,用于检测redis是否连接成功
go get github.com/redis/go-redis/v9
1
RedisConfig:
Host: "127.0.0.1:6379"
Pass: "mszlu"
Type: "node"
Tls: false
NonBlock: false
PingTimeout: 1s
1
2
3
4
5
6
7
2
3
4
5
6
7
package db
import "github.com/zeromicro/go-zero/core/stores/redis"
func NewRedis(conf redis.RedisConf) *redis.Redis {
return redis.MustNewRedis(conf)
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
servicecontext中注入:
package svc
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"user-api/internal/config"
"user-api/internal/db"
)
type ServiceContext struct {
Config config.Config
Conn sqlx.SqlConn
RedisClient *redis.Redis
}
func NewServiceContext(c config.Config) *ServiceContext {
sqlConn := db.NewMysql(c.MysqlConfig)
redisClient := db.NewRedis(c.RedisConfig)
return &ServiceContext{
Config: c,
Conn: sqlConn,
RedisClient: redisClient,
}
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
改造登录代码:
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
// todo: add your logic here and delete this line
userModel := user.NewUserModel(l.svcCtx.Conn)
u, err := userModel.FindByUsernameAndPwd(l.ctx, req.Username, req.Password)
if err != nil {
l.Logger.Error(err)
return nil, biz.DBError
}
if u == nil {
return nil, biz.NameOrPwdError
}
//登录成功 生成token
secret := l.svcCtx.Config.Auth.Secret
expire := l.svcCtx.Config.Auth.Expire
token, err := biz.GetJwtToken(secret, time.Now().Unix(), expire, u.Id)
if err != nil {
l.Logger.Error(err)
return nil, biz.TokenError
}
//把token存入redis
err = l.svcCtx.RedisClient.SetexCtx(context.Background(), "token:"+token, strconv.FormatInt(u.Id, 10), int(expire))
if err != nil {
return nil, biz.RedisError
}
resp = &types.LoginResp{
Token: token,
}
return
}
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
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
测试
4.3 集群
集群方式只需要改配置即可,go-zero对集群和单机做了适配。
go-zero内置redis的缺陷:
- 不能选择redis数据库
- 不能配置连接池
- 对原生go-redis配置项不能自定义
实际开发中,如果对配置项需要精细化控制,建议直接使用go-redis库即可,并且自行对go-redis进行封装