mirror of
https://git.fightbot.fun/hxuanyu/BingPaper.git
synced 2026-02-15 08:49:33 +08:00
前端公共部分开发完成,支持图片展示功能
This commit is contained in:
255
webapp/CORS_CONFIG.md
Normal file
255
webapp/CORS_CONFIG.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# Go 后端 CORS 配置示例
|
||||
|
||||
## 问题说明
|
||||
|
||||
开发环境中,前端运行在 `http://localhost:5173`,后端运行在 `http://localhost:8080`。
|
||||
由于跨域限制,需要在后端配置 CORS 才能正常访问 API。
|
||||
|
||||
## 使用 Gin 框架
|
||||
|
||||
### 1. 安装 CORS 中间件
|
||||
|
||||
```bash
|
||||
go get github.com/gin-contrib/cors
|
||||
```
|
||||
|
||||
### 2. 配置 CORS
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-contrib/cors"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// 开发环境:配置 CORS
|
||||
if gin.Mode() == gin.DebugMode {
|
||||
config := cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:5173"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
r.Use(cors.New(config))
|
||||
}
|
||||
|
||||
// API 路由
|
||||
api := r.Group("/api/v1")
|
||||
{
|
||||
// 图片相关
|
||||
api.GET("/images", getImages)
|
||||
api.GET("/image/today/meta", getTodayImageMeta)
|
||||
api.GET("/image/date/:date/meta", getImageMetaByDate)
|
||||
api.GET("/image/random/meta", getRandomImageMeta)
|
||||
|
||||
// 管理员相关
|
||||
api.POST("/admin/login", adminLogin)
|
||||
// ... 其他路由
|
||||
}
|
||||
|
||||
// 生产环境:静态文件服务
|
||||
if gin.Mode() == gin.ReleaseMode {
|
||||
r.Static("/assets", "./web/assets")
|
||||
r.StaticFile("/", "./web/index.html")
|
||||
|
||||
// SPA fallback
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.File("./web/index.html")
|
||||
})
|
||||
}
|
||||
|
||||
r.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 更灵活的 CORS 配置
|
||||
|
||||
```go
|
||||
// 根据环境变量动态配置
|
||||
func setupCORS(r *gin.Engine) {
|
||||
allowOrigins := os.Getenv("ALLOW_ORIGINS")
|
||||
if allowOrigins == "" {
|
||||
allowOrigins = "http://localhost:5173"
|
||||
}
|
||||
|
||||
origins := strings.Split(allowOrigins, ",")
|
||||
|
||||
config := cors.Config{
|
||||
AllowOrigins: origins,
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
|
||||
r.Use(cors.New(config))
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// 只在开发环境启用 CORS
|
||||
if gin.Mode() == gin.DebugMode {
|
||||
setupCORS(r)
|
||||
}
|
||||
|
||||
// ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
## 使用标准库
|
||||
|
||||
如果不使用 Gin 框架,可以手动实现 CORS:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func corsMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 设置 CORS 头
|
||||
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Access-Control-Max-Age", "43200") // 12 hours
|
||||
|
||||
// 处理预检请求
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// 注册路由
|
||||
mux.HandleFunc("/api/v1/images", getImages)
|
||||
// ... 其他路由
|
||||
|
||||
// 包装 CORS 中间件
|
||||
handler := corsMiddleware(mux)
|
||||
|
||||
http.ListenAndServe(":8080", handler)
|
||||
}
|
||||
```
|
||||
|
||||
## 配置文件方式
|
||||
|
||||
可以通过配置文件管理 CORS 设置:
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
server:
|
||||
port: 8080
|
||||
mode: debug # debug 或 release
|
||||
|
||||
cors:
|
||||
enabled: true
|
||||
allow_origins:
|
||||
- http://localhost:5173
|
||||
- http://127.0.0.1:5173
|
||||
allow_methods:
|
||||
- GET
|
||||
- POST
|
||||
- PUT
|
||||
- PATCH
|
||||
- DELETE
|
||||
- OPTIONS
|
||||
allow_headers:
|
||||
- Origin
|
||||
- Content-Type
|
||||
- Accept
|
||||
- Authorization
|
||||
allow_credentials: true
|
||||
max_age: 43200
|
||||
```
|
||||
|
||||
```go
|
||||
type CORSConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
AllowOrigins []string `yaml:"allow_origins"`
|
||||
AllowMethods []string `yaml:"allow_methods"`
|
||||
AllowHeaders []string `yaml:"allow_headers"`
|
||||
AllowCredentials bool `yaml:"allow_credentials"`
|
||||
MaxAge int `yaml:"max_age"`
|
||||
}
|
||||
|
||||
func setupCORSFromConfig(r *gin.Engine, cfg *CORSConfig) {
|
||||
if !cfg.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
config := cors.Config{
|
||||
AllowOrigins: cfg.AllowOrigins,
|
||||
AllowMethods: cfg.AllowMethods,
|
||||
AllowHeaders: cfg.AllowHeaders,
|
||||
AllowCredentials: cfg.AllowCredentials,
|
||||
MaxAge: time.Duration(cfg.MaxAge) * time.Second,
|
||||
}
|
||||
|
||||
r.Use(cors.New(config))
|
||||
}
|
||||
```
|
||||
|
||||
## 安全建议
|
||||
|
||||
1. **生产环境不要启用 CORS**:生产环境前后端在同一域名下,不需要 CORS
|
||||
2. **限制允许的源**:不要使用 `*`,明确指定允许的域名
|
||||
3. **使用环境变量**:允许的源应该通过环境变量配置,不要硬编码
|
||||
4. **限制方法和头**:只允许必要的 HTTP 方法和请求头
|
||||
5. **注意凭证**:如果使用 `AllowCredentials: true`,不能使用通配符源
|
||||
|
||||
## 测试 CORS 配置
|
||||
|
||||
可以使用 curl 测试 CORS:
|
||||
|
||||
```bash
|
||||
# 测试预检请求
|
||||
curl -X OPTIONS http://localhost:8080/api/v1/images \
|
||||
-H "Origin: http://localhost:5173" \
|
||||
-H "Access-Control-Request-Method: GET" \
|
||||
-v
|
||||
|
||||
# 测试实际请求
|
||||
curl -X GET http://localhost:8080/api/v1/images \
|
||||
-H "Origin: http://localhost:5173" \
|
||||
-v
|
||||
```
|
||||
|
||||
应该能看到响应头中包含:
|
||||
```
|
||||
Access-Control-Allow-Origin: http://localhost:5173
|
||||
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
|
||||
Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization
|
||||
Access-Control-Allow-Credentials: true
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 为什么要区分开发和生产环境?
|
||||
A: 生产环境前后端部署在同一域名下,不需要 CORS。只在开发环境需要。
|
||||
|
||||
### Q: 可以使用代理吗?
|
||||
A: 可以在 Vite 中配置代理,但不推荐。直接配置 CORS 更接近生产环境,便于发现问题。
|
||||
|
||||
### Q: 如何处理认证?
|
||||
A: 如果使用 Cookie 或需要发送凭证,必须设置 `AllowCredentials: true` 和明确的源。
|
||||
|
||||
### Q: 预检请求是什么?
|
||||
A: 浏览器在某些跨域请求前会发送 OPTIONS 请求,询问服务器是否允许该跨域请求。
|
||||
Reference in New Issue
Block a user