package main import ( "errors" "net/http" "strings" "github.com/duke-git/lancet/v2/validator" "github.com/gin-gonic/gin" "resty.dev/v3" ) // newServer 创建并配置 HTTP 服务。 func newServer(db *DB, client *resty.Client) *gin.Engine { gin.SetMode(gin.ReleaseMode) server := gin.Default() server.Use(corsMiddleware()) registerRoutes(server, db, client) return server } // corsMiddleware 允许浏览器跨域访问接口。 func corsMiddleware() gin.HandlerFunc { return func(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") ctx.Header("Access-Control-Allow-Methods", "GET, OPTIONS") ctx.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization") if ctx.Request.Method == http.MethodOptions { ctx.AbortWithStatus(http.StatusNoContent) return } ctx.Next() } } // registerRoutes 注册当前服务公开的 HTTP 路由。 func registerRoutes(server *gin.Engine, db *DB, client *resty.Client) { server.GET("/", handleArticleRequest(db, client)) } // handleArticleRequest 处理文章查询请求:校验参数,读取缓存或抓取源站。 func handleArticleRequest(db *DB, client *resty.Client) gin.HandlerFunc { return func(ctx *gin.Context) { articleID := strings.TrimSpace(ctx.Query("article_id")) if articleID == "" { ctx.JSON(http.StatusNotFound, response{Ok: false}) return } if !validator.IsNumberStr(articleID) { ctx.JSON(http.StatusBadRequest, response{ Ok: false, Error: "article_id 应为数字", }) return } html, err := getArticleHTML(db, client, articleID) if err != nil { ctx.JSON(articleErrorStatusCode(err), createErr(err)) return } ctx.JSON(http.StatusOK, response{ Ok: true, Html: html, }) } } // articleErrorStatusCode 将文章抓取错误映射为合适的 HTTP 状态码。 func articleErrorStatusCode(err error) int { if errors.Is(err, errArticleContentNotFound) || errors.Is(err, errArticleContentParseFailed) { return http.StatusNotFound } return http.StatusInternalServerError }