Skip to content

Commit c545a23

Browse files
authored
Merge pull request #11 from go-spring-projects/feat-redesign-renderer
Refactor web.Renderer interface
2 parents 230f3cb + 21c4010 commit c545a23

File tree

5 files changed

+79
-33
lines changed

5 files changed

+79
-33
lines changed

web/bind.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@
1717
package web
1818

1919
import (
20-
"context"
2120
"fmt"
2221
"net/http"
2322
"reflect"
2423

2524
"github.com/go-spring-projects/go-spring/internal/utils"
2625
"github.com/go-spring-projects/go-spring/web/binding"
27-
"github.com/go-spring-projects/go-spring/web/render"
2826
)
2927

3028
type Renderer interface {
31-
Render(ctx context.Context, err error, result interface{}) render.Renderer
29+
Render(ctx *Context, err error, result interface{})
3230
}
3331

34-
type RendererFunc func(ctx context.Context, err error, result interface{}) render.Renderer
32+
type RendererFunc func(ctx *Context, err error, result interface{})
3533

36-
func (fn RendererFunc) Render(ctx context.Context, err error, result interface{}) render.Renderer {
37-
return fn(ctx, err, result)
34+
func (fn RendererFunc) Render(ctx *Context, err error, result interface{}) {
35+
fn(ctx, err, result)
3836
}
3937

4038
// Bind convert fn to HandlerFunc.
@@ -76,9 +74,9 @@ func Bind(fn interface{}, render Renderer) http.HandlerFunc {
7674

7775
defer func() {
7876
if nil != request.MultipartForm {
79-
request.MultipartForm.RemoveAll()
77+
_ = request.MultipartForm.RemoveAll()
8078
}
81-
request.Body.Close()
79+
_ = request.Body.Close()
8280
}()
8381

8482
var returnValues []reflect.Value
@@ -93,7 +91,7 @@ func Bind(fn interface{}, render Renderer) http.HandlerFunc {
9391
}
9492

9593
// render error response
96-
render.Render(ctx, err, nil).Render(writer)
94+
render.Render(webCtx, err, nil)
9795
}
9896
}()
9997

@@ -144,7 +142,7 @@ func Bind(fn interface{}, render Renderer) http.HandlerFunc {
144142
}
145143

146144
// render response
147-
render.Render(ctx, err, result).Render(writer)
145+
render.Render(webCtx, err, result)
148146
}
149147
}
150148

web/binding/binding.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package binding
1919

2020
import (
21+
"errors"
2122
"fmt"
2223
"io"
2324
"mime"
@@ -30,6 +31,9 @@ import (
3031
"github.com/go-spring-projects/go-spring/conf"
3132
)
3233

34+
var ErrBinding = errors.New("binding failed")
35+
var ErrValidate = errors.New("validate failed")
36+
3337
const (
3438
MIMEApplicationJSON = "application/json"
3539
MIMEApplicationXML = "application/xml"
@@ -93,12 +97,17 @@ func RegisterBodyBinder(mime string, binder BodyBinder) {
9397

9498
func Bind(i interface{}, r Request) error {
9599
if err := bindScope(i, r); err != nil {
96-
return err
100+
return fmt.Errorf("%w: %v", ErrBinding, err)
97101
}
102+
98103
if err := bindBody(i, r); err != nil {
99-
return err
104+
return fmt.Errorf("%w: %v", ErrBinding, err)
100105
}
101-
return conf.ValidateStruct(i)
106+
107+
if err := conf.ValidateStruct(i); nil != err {
108+
return fmt.Errorf("%w: %v", ErrValidate, err)
109+
}
110+
return nil
102111
}
103112

104113
func bindBody(i interface{}, r Request) error {

web/error.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package web
1919
import (
2020
"fmt"
2121
"net/http"
22-
"strings"
2322
)
2423

2524
type HttpError struct {
@@ -31,10 +30,10 @@ func (e HttpError) Error() string {
3130
return fmt.Sprintf("%d: %s", e.Code, e.Message)
3231
}
3332

34-
func Error(code int, msg ...string) HttpError {
33+
func Error(code int, format string, args ...interface{}) HttpError {
3534
var message = http.StatusText(code)
36-
if len(msg) > 0 {
37-
message = strings.Join(msg, ",")
35+
if len(format) > 0 {
36+
message = fmt.Sprintf(format, args...)
3837
}
3938
return HttpError{Code: code, Message: message}
4039
}

web/examples/greeting/main.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"log/slog"
2323
"math/rand"
2424
"mime/multipart"
25+
"net/http"
2526
"time"
2627

2728
"github.com/go-spring-projects/go-spring/gs"
@@ -38,6 +39,19 @@ func (g *Greeting) OnInit(ctx context.Context) error {
3839
g.Server.Bind("/greeting", g.Greeting)
3940
g.Server.Bind("/health", g.Health)
4041
g.Server.Bind("/user/register/{username}/{password}", g.Register)
42+
g.Server.Bind("/user/password", g.UpdatePassword)
43+
44+
g.Server.Use(func(handler http.Handler) http.Handler {
45+
46+
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
47+
48+
start := time.Now()
49+
handler.ServeHTTP(writer, request)
50+
g.Logger.Info("http handle cost",
51+
slog.String("path", request.URL.Path), slog.Duration("cost", time.Since(start)))
52+
})
53+
})
54+
4155
return nil
4256
}
4357

@@ -57,13 +71,13 @@ func (g *Greeting) Health(ctx context.Context) (string, error) {
5771
func (g *Greeting) Register(
5872
ctx context.Context,
5973
req struct {
60-
Username string `path:"username"` // 用户名
61-
Password string `path:"password"` // 密码
62-
HeadImg *multipart.FileHeader `form:"headImg"` // 上传头像
63-
Captcha string `form:"captcha"` // 验证码
64-
UserAgent string `header:"User-Agent"` // 用户代理
65-
Ad string `query:"ad"` // 推广ID
66-
Token string `cookie:"token"` // cookie参数
74+
Username string `path:"username" expr:"len($)>6 && len($)<20"` // username
75+
Password string `path:"password" expr:"len($)>6 && len($)<20"` // password
76+
HeadImg *multipart.FileHeader `form:"headImg"` // upload head image
77+
Captcha string `form:"captcha" expr:"len($)==4"` // captcha
78+
UserAgent string `header:"User-Agent"` // user agent
79+
Ad string `query:"ad"` // AD
80+
Token string `cookie:"token"` // token
6781
},
6882
) string {
6983
g.Logger.Info("register user",
@@ -79,6 +93,17 @@ func (g *Greeting) Register(
7993
return "ok"
8094
}
8195

96+
func (g *Greeting) UpdatePassword(
97+
ctx context.Context,
98+
req struct {
99+
Password string `json:"password" expr:"len($) > 6 && len($) < 20"` // password
100+
Token string `cookie:"token"` // token
101+
},
102+
) string {
103+
g.Logger.Info("change password", slog.String("password", req.Password))
104+
return "ok"
105+
}
106+
82107
func main() {
83108
gs.Object(new(Greeting))
84109

web/server.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"errors"
2323
"net/http"
2424

25-
"github.com/go-spring-projects/go-spring/web/render"
25+
"github.com/go-spring-projects/go-spring/web/binding"
2626
)
2727

2828
// A Server defines parameters for running an HTTP server.
@@ -54,13 +54,7 @@ func NewServer(router *Router, options Options) *Server {
5454
}
5555
}
5656

57-
var jsonRenderer = func(ctx context.Context, err error, result interface{}) render.Renderer {
58-
59-
type jsonResponse struct {
60-
Code int `json:"code"`
61-
Message string `json:"message,omitempty"`
62-
Data interface{} `json:"data"`
63-
}
57+
var jsonRenderer = func(ctx *Context, err error, result interface{}) {
6458

6559
var code = 0
6660
var message = ""
@@ -72,10 +66,20 @@ func NewServer(router *Router, options Options) *Server {
7266
} else {
7367
code = http.StatusInternalServerError
7468
message = err.Error()
69+
70+
if errors.Is(err, binding.ErrBinding) || errors.Is(err, binding.ErrValidate) {
71+
code = http.StatusBadRequest
72+
}
7573
}
7674
}
7775

78-
return render.JsonRenderer{Data: jsonResponse{Code: code, Message: message, Data: result}}
76+
type response struct {
77+
Code int `json:"code"`
78+
Message string `json:"message,omitempty"`
79+
Data interface{} `json:"data"`
80+
}
81+
82+
ctx.JSON(http.StatusOK, response{Code: code, Message: message, Data: result})
7983
}
8084

8185
return &Server{
@@ -121,6 +125,11 @@ func (s *Server) Shutdown(ctx context.Context) error {
121125
return s.httpSvr.Shutdown(ctx)
122126
}
123127

128+
// Router returns the server router.
129+
func (s *Server) Router() *Router {
130+
return s.router
131+
}
132+
124133
// NotFound to be used when no route matches.
125134
// This can be used to render your own 404 Not Found errors.
126135
func (s *Server) NotFound(handler http.Handler) {
@@ -138,6 +147,12 @@ func (s *Server) Renderer(renderer Renderer) {
138147
s.renderer = renderer
139148
}
140149

150+
// Use appends a MiddlewareFunc to the chain.
151+
// Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router.
152+
func (s *Server) Use(mwf ...MiddlewareFunc) {
153+
s.router.Use(mwf...)
154+
}
155+
141156
// Match attempts to match the given request against the router's registered routes.
142157
//
143158
// If the request matches a route of this router or one of its subrouters the Route,

0 commit comments

Comments
 (0)