-
Notifications
You must be signed in to change notification settings - Fork 3
/
respond.go
executable file
·141 lines (122 loc) · 3.31 KB
/
respond.go
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package httpKit
import (
"fmt"
"github.com/richelieu-yang/chimera/v3/src/core/strKit"
"github.com/richelieu-yang/chimera/v3/src/file/fileKit"
"github.com/richelieu-yang/chimera/v3/src/serialize/json/jsonKit"
"net/http"
"net/url"
"path/filepath"
"unicode"
)
// Status 设置响应的http状态码
/*
PS:
(1) 不建议多次设置 http状态码;
(2) 如果多次设置的话,感觉 第一次设置的值 会生效.
@param code -1: 不设置http状态码
*/
func Status(w http.ResponseWriter, code int) {
if code <= 0 {
return
}
w.WriteHeader(code)
}
// RespondString
/*
参考: gin里面的 Context.String() .
*/
func RespondString(w http.ResponseWriter, code int, format string, values ...any) error {
data := strKit.StringToBytes(fmt.Sprintf(format, values...))
return RespondData(w, code, PlainContentType, data)
}
func RespondStringData(w http.ResponseWriter, code int, data []byte) error {
return RespondData(w, code, PlainContentType, data)
}
// RespondJson
/*
参考: gin里面的 Context.JSON() .
*/
func RespondJson(w http.ResponseWriter, code int, obj any) error {
data, err := jsonKit.Marshal(obj)
if err != nil {
return err
}
return RespondData(w, code, JsonContentType, data)
}
// RespondFile 响应文件
/*
参考: gin里面的 Context.File() 和 Context.FileAttachment() .
@param filePath 文件路径
@param fileName 文件名(可以为"",此时将从 传参filePath 中获取)
@return 如果不为nil,建议输出到控制台
*/
func RespondFile(w http.ResponseWriter, r *http.Request, code int, filePath, fileName string) error {
if err := fileKit.AssertExistAndIsFile(filePath); err != nil {
return err
}
if fileName == "" {
fileName = filepath.Base(filePath)
}
Status(w, code)
// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters
isASCII := func(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return false
}
}
return true
}
if isASCII(fileName) {
w.Header().Set("Content-Disposition", `attachment; filename="`+fileName+`"`)
} else {
w.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(fileName))
}
http.ServeFile(w, r, filePath)
return nil
}
// RespondData 响应字节流(二进制流)
/*
参考: gin里面的 Context.Content() .
@return 如果不为nil,建议输出到控制台
*/
func RespondData(w http.ResponseWriter, code int, contentType string, data []byte) error {
if strKit.IsEmpty(contentType) {
contentType = fileKit.DetectContentType(data)
}
Status(w, code)
setContentType(w, []string{contentType})
if !bodyAllowedForStatus(code) {
return nil
}
_, err := w.Write(data)
return err
}
// setContentType
/*
PS: copy from gin/render/render.go
*/
func setContentType(w http.ResponseWriter, value []string) {
header := w.Header()
if val := header["Content-Type"]; len(val) == 0 {
header["Content-Type"] = value
}
}
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
/*
PS:
(1) copy from gin/context.go
(2) @return 在对应http状态码的情况下,是否允许写内容?
*/
func bodyAllowedForStatus(status int) bool {
switch {
case status >= 100 && status <= 199:
return false
case status == http.StatusNoContent:
return false
case status == http.StatusNotModified:
return false
}
return true
}