Skip to content

Commit

Permalink
refactor(server): 添加 httpConfig.Recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
caixw committed Apr 15, 2024
1 parent 7dee2b3 commit 46079f9
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 41 deletions.
3 changes: 2 additions & 1 deletion locales/locales.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ var Locales = []fs.FS{
scheduled.Locales,
}

// 一些多处用到的翻译项
//---------------------------- 以下为本地化的文本内容 -----------------------------

const (
InvalidFormat = localeutil.StringPhrase("invalid format")
InvalidValue = localeutil.StringPhrase("invalid value")
Expand Down
5 changes: 2 additions & 3 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"net/http"
"strconv"

"github.com/issue9/logs/v7"
"github.com/issue9/mux/v8"
"github.com/issue9/mux/v8/group"
"github.com/issue9/mux/v8/header"
Expand Down Expand Up @@ -98,8 +97,8 @@ func (r *Routers) Use(m ...Middleware) { r.g.Use(m...) }

// Recovery 在路由奔溃之后的处理方式
//
// 相对于 [mux.Recovery] 的相关功能,提供了对 [web.NewError] 错误的处理。
func Recovery(status int, l *logs.Logger) RouterOption {
// 相对于 [mux.Recovery],提供了对 [web.NewError] 错误的处理。
func Recovery(status int, l *Logger) RouterOption {
return mux.Recovery(func(w http.ResponseWriter, msg any) {
err, ok := msg.(error)
if !ok {
Expand Down
6 changes: 4 additions & 2 deletions server/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ func (conf *configOf[T]) buildCache() *web.FieldError {
}
conf.cache = drv
if job != nil {
conf.init = append(conf.init, func(s web.Server) {
s.Services().AddTicker(locales.RecycleLocalCache, job.Job, job.Ticker, false, false)
conf.init = append(conf.init, func(o *Options) {
o.Init = append(o.Init, func(s web.Server) {
s.Services().AddTicker(locales.RecycleLocalCache, job.Job, job.Ticker, false, false)
})
})
}

Expand Down
33 changes: 24 additions & 9 deletions server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import (
"github.com/issue9/web/selector"
)

// 在项目正式运行之后,对于配置项的修改应该慎之又慎,
// 不当的修改可能导致项目运行过程中出错,比如改变了唯一 ID
// 的生成规则,可能会导致新生成的唯一 ID 与之前的 ID 重复。
type configOf[T any] struct {
XMLName struct{} `yaml:"-" json:"-" xml:"web"`

Expand Down Expand Up @@ -88,7 +91,7 @@ type configOf[T any] struct {
// - date 日期格式,默认值;
// - string 普通的字符串;
// - number 数值格式;
// NOTE: 一旦运行在生产环境,就不应该修改此属性,新的生成器无法保证生成的 ID 不会与之前的重复
// NOTE: 一旦运行在生产环境,就不应该修改此属性,除非能确保新的函数生成的 ID 不与之前生成的 ID 重复
IDGenerator string `yaml:"idGenerator,omitempty" json:"idGenerator,omitempty" xml:"idGenerator,omitempty"`
idGenerator IDGenerator

Expand Down Expand Up @@ -120,7 +123,7 @@ type configOf[T any] struct {
User *T `yaml:"user,omitempty" json:"user,omitempty" xml:"user,omitempty"`

// 由其它选项生成的初始化方法
init []func(web.Server)
init []func(*Options)
}

// LoadOptions 从配置文件初始化 [Options] 对象
Expand Down Expand Up @@ -157,27 +160,33 @@ func LoadOptions[T any](configDir, filename string) (*Options, *T, error) {
return nil, nil, web.NewStackError(err)
}

return &Options{
o := &Options{
Config: conf.config,
Location: conf.location,
Cache: conf.cache,
HTTPServer: conf.HTTP.httpServer,
Logs: conf.Logs.logs,
Language: conf.languageTag,
RoutersOptions: conf.HTTP.routersOptions,
RoutersOptions: make([]web.RouterOption, 0, 5),
IDGenerator: conf.idGenerator,
RequestIDKey: conf.HTTP.RequestID,
Compressions: conf.compressors,
Mimetypes: conf.mimetypes,
ProblemTypePrefix: conf.ProblemTypePrefix,
OnRender: conf.onRender,
Init: conf.init,
}, conf.User, nil
Init: make([]web.PluginFunc, 0, 5),
}

for _, i := range conf.init {
i(o)
}

return o, conf.User, nil
}

func (conf *configOf[T]) SanitizeConfig() *web.FieldError {
if conf.MemoryLimit > 0 {
conf.init = append(conf.init, func(web.Server) { debug.SetMemoryLimit(conf.MemoryLimit) })
conf.init = append(conf.init, func(*Options) { debug.SetMemoryLimit(conf.MemoryLimit) })
}

if err := conf.buildCache(); err != nil {
Expand All @@ -191,7 +200,9 @@ func (conf *configOf[T]) SanitizeConfig() *web.FieldError {
if err := conf.Logs.build(); err != nil {
return err.AddFieldParent("logs")
}
conf.init = append(conf.init, func(s web.Server) { s.OnClose(conf.Logs.cleanup...) })
conf.init = append(conf.init, func(o *Options) {
o.Init = append(o.Init, func(s web.Server) { s.OnClose(conf.Logs.cleanup...) })
})

if conf.Language != "" {
tag, err := language.Parse(conf.Language)
Expand Down Expand Up @@ -234,7 +245,11 @@ func (conf *configOf[T]) SanitizeConfig() *web.FieldError {
f, srv := g()
conf.idGenerator = f
if srv != nil {
conf.init = append(conf.init, func(s web.Server) { s.Services().Add(locales.UniqueIdentityGenerator, srv) })
conf.init = append(conf.init, func(o *Options) {
o.Init = append(o.Init, func(s web.Server) {
s.Services().Add(locales.UniqueIdentityGenerator, srv)
})
})
}
}

Expand Down
3 changes: 2 additions & 1 deletion server/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ func TestLoadOptions(t *testing.T) {
a := assert.New(t, false)

s, data, err := LoadOptions[empty]("./testdata", "web.yaml")
a.NotError(err).NotNil(s).Nil(data)
a.NotError(err).NotNil(s).Nil(data).
Length(s.Init, 3) // cache, idgen, logs

s, data, err = LoadOptions[empty]("./testdata/not-exists", "web.yaml")
a.ErrorIs(err, fs.ErrNotExist).Nil(s).Nil(data)
Expand Down
49 changes: 31 additions & 18 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type (

// x-request-id 的报头名称
//
// 如果为空,则采用 [RequestIDKey] 作为默认值。
// 如果为空,则采用 [header.XRequestID] 作为默认值。
RequestID string `yaml:"requestID,omitempty" json:"requestID,omitempty" xml:"requestID,omitempty"`

// 网站的域名证书
Expand All @@ -50,6 +50,11 @@ type (
ReadHeaderTimeout duration `yaml:"readHeaderTimeout,omitempty" json:"readHeaderTimeout,omitempty" xml:"readHeaderTimeout,attr,omitempty"`
MaxHeaderBytes int `yaml:"maxHeaderBytes,omitempty" json:"maxHeaderBytes,omitempty" xml:"maxHeaderBytes,attr,omitempty"`

// Recovery 拦截 panic 时反馈给客户端的状态码
//
// NOTE: 这些设置对所有路径均有效,但会被 [web.Routers.New] 的参数修改。
Recovery int `yaml:"recovery,omitempty" json:"recovery,omitempty" xml:"recovery,attr,omitempty"`

// 自定义报头功能
//
// 报头会输出到包括 404 在内的所有请求返回。可以为空。
Expand All @@ -61,19 +66,20 @@ type (

// 自定义[跨域请求]设置项
//
// 这些设置对所有路径均有效。
// NOTE: 这些设置对所有路径均有效,但会被 [web.Routers.New] 的参数修改
//
// [跨域请求]: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/cors
CORS *corsConfig `yaml:"cors,omitempty" json:"cors,omitempty" xml:"cors,omitempty"`

// Trace 是否启用 TRACE 请求
//
// 默认为 false。
//
// NOTE: 这些设置对所有路径均有效,但会被 [web.Routers.New] 的参数修改。
Trace bool `yaml:"trace,omitempty" json:"trace,omitempty" xml:"trace,omitempty"`

routersOptions []mux.Option
init func(web.Server)
httpServer *http.Server
init func(*Options)
httpServer *http.Server
}

headerConfig struct {
Expand Down Expand Up @@ -177,20 +183,23 @@ func (h *httpConfig) sanitize() *web.FieldError {
return err
}

if len(h.Headers) > 0 {
h.init = func(s web.Server) {
s.Routers().Use(web.MiddlewareFunc(func(next web.HandlerFunc) web.HandlerFunc {
return func(ctx *web.Context) web.Responser {
for _, hh := range h.Headers {
ctx.Header().Add(hh.Key, hh.Value)
h.init = func(o *Options) {
if len(h.Headers) > 0 {
o.Init = append(o.Init, func(s web.Server) {
s.Routers().Use(web.MiddlewareFunc(func(next web.HandlerFunc) web.HandlerFunc {
return func(ctx *web.Context) web.Responser {
for _, hh := range h.Headers {
ctx.Header().Add(hh.Key, hh.Value)
}
return next(ctx)
}
return next(ctx)
}
}))
}))
})
}

h.buildRoutersOptions(o)
}

h.buildRoutersOptions()
h.buildHTTPServer()
return nil
}
Expand All @@ -207,8 +216,12 @@ func (h *httpConfig) buildHTTPServer() {
}
}

func (h *httpConfig) buildRoutersOptions() {
opt := make([]web.RouterOption, 0, 2)
func (h *httpConfig) buildRoutersOptions(o *Options) {
opt := make([]web.RouterOption, 0, 3)

if h.Recovery > 0 {
opt = append(opt, web.Recovery(h.Recovery, o.logs.ERROR()))
}

if h.CORS != nil {
c := h.CORS
Expand All @@ -217,7 +230,7 @@ func (h *httpConfig) buildRoutersOptions() {

opt = append(opt, mux.Trace(h.Trace))

h.routersOptions = opt
o.RoutersOptions = append(o.RoutersOptions, opt...)
}

func (h *httpConfig) buildTLSConfig() *web.FieldError {
Expand Down
10 changes: 3 additions & 7 deletions server/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type (

// 路由选项
//
// 如果为空,会添加 [web.Recovery] 作为默认值
// 可以为空
RoutersOptions []web.RouterOption

// 指定获取 x-request-id 内容的报头名
Expand Down Expand Up @@ -139,8 +139,8 @@ type (

// Init 其它的一些初始化操作
//
// 在此可以让用户能实际操作 [Server] 之前对其进行一些修改。
Init []func(web.Server)
// 在此可以让用户能实际操作 [web.Server] 之前对其进行一些修改。
Init []web.PluginFunc

// 以下微服务相关的设置

Expand Down Expand Up @@ -262,10 +262,6 @@ func sanitizeOptions(o *Options, t int) (*Options, *config.FieldError) {
return nil, err
}

if len(o.RoutersOptions) == 0 {
o.RoutersOptions = []web.RouterOption{web.Recovery(http.StatusInternalServerError, o.logs.ERROR())}
}

if o.RequestIDKey == "" {
o.RequestIDKey = header.XRequestID
}
Expand Down

0 comments on commit 46079f9

Please sign in to comment.