You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ims/util/rsp/rsp.go

143 lines
3.3 KiB

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)
}