package rsp import ( "bytes" "encoding/json" "net/http" "zestack.dev/slim" ) var ( // TextMarshaller 将 Map 转化成文本格式用于响应给请求者 TextMarshaller func(map[string]any) (string, error) // HtmlMarshaller 将 Map 转换成超文本格式用于响应给请求者 HtmlMarshaller func(map[string]any) (string, error) // JsonpCallbacks 默认 JSONP 查询的字段 JsonpCallbacks []string // DefaultJsonpCallback 默认查询的 JSONP 函数字段名 DefaultJsonpCallback string ) func init() { TextMarshaller = toText HtmlMarshaller = toText JsonpCallbacks = []string{"callback", "cb", "jsonp"} DefaultJsonpCallback = "callback" } func toText(m map[string]any) (string, error) { buf := &bytes.Buffer{} enc := json.NewEncoder(buf) enc.SetEscapeHTML(true) if err := enc.Encode(m); err != nil { return "", err } return buf.String(), nil } func respond(c slim.Context, o *response) (err error) { defer func() { if err != nil { c.Logger().Error(err.Error()) } }() // 如果已经输出过,就忽略 if c.Written() { return nil } // 设置报头 if o.headers != nil { for key, value := range o.headers { c.SetHeader(key, value) } } // 设置 cookie if o.cookies != nil { for _, cookie := range o.cookies { c.SetCookie(cookie) } } m, status := o.result(c) // HEAD 请求没有结果 r := c.Request() if r.Method == http.MethodHead { return c.NoContent(status) } // 根据报头响应不同的格式 switch c.Accepts("html", "json", "jsonp", "xml", "text", "text/*") { case "html": var html string if html, err = HtmlMarshaller(m); err == nil { err = c.HTML(status, html) } case "json": err = c.JSON(status, m) case "jsonp": qs := c.Request().URL.Query() for _, name := range JsonpCallbacks { if cb := qs.Get(name); cb != "" { err = c.JSONP(status, cb, m) return } } err = c.JSONP(status, DefaultJsonpCallback, m) case "xml": err = c.XML(status, m) case "text", "text/*": var text string if text, err = TextMarshaller(m); err == nil { err = c.String(status, text) } default: err = c.JSON(status, m) } return } // Respond 自定义响应 func Respond(c slim.Context, opts ...Option) error { o := response{status: http.StatusOK} for _, option := range opts { option(&o) } return respond(c, &o) } // Ok 响应成功请求 func Ok(c slim.Context, data any) error { return Respond(c, Data(data)) } // Created 表示数据创建成功 func Created(c slim.Context, data any) error { return Respond(c, Data(data), StatusCode(http.StatusCreated)) } // Deleted 表示数据删除成功 func Deleted(c slim.Context, data ...any) error { if len(data) > 0 { return Respond(c, StatusCode(http.StatusOK), Data(data[0])) } return Respond(c, StatusCode(http.StatusOK)) } // NotFound 响应访问的资源不存在 func NotFound(c slim.Context) error { return Respond(c, StatusCode(http.StatusNotFound)) } // Accepted 响应一个异步操作,比如任务调度等 func Accepted(c slim.Context, data any) error { return Respond(c, Data(data), StatusCode(http.StatusAccepted)) } // Fail 响应一个错误 func Fail(c slim.Context, err error, opts ...Option) error { o := response{status: http.StatusInternalServerError} for _, option := range opts { option(&o) } o.err = err return respond(c, &o) }