diff --git a/.example/net/ghttp/server/middleware/issue355.go b/.example/net/ghttp/server/middleware/issue355.go new file mode 100644 index 000000000..0470c4af4 --- /dev/null +++ b/.example/net/ghttp/server/middleware/issue355.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.BindMiddlewareDefault(func(r *ghttp.Request) { + fmt.Println("cors") + r.Response.CORSDefault() + r.Middleware.Next() + }) + s.BindHandler("/api/captcha", func(r *ghttp.Request) { + r.Response.Write("captcha") + }) + s.SetPort(8010) + s.Run() +} diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 39d1b3631..f35409f05 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -15,33 +15,33 @@ import ( "github.com/gogf/gf/util/gutil" ) -// 中间件对象 +// Middleware is the plugin for request handling. type Middleware struct { - served bool // 是否带有请求服务函数,用以识别是否404 - request *Request // 请求对象 + served bool // Is the request served, which is used for checking response status 404. + request *Request // The request object pointer. } -// 执行下一个请求流程处理函数 +// Next calls the next workflow handler. func (m *Middleware) Next() { item := (*handlerParsedItem)(nil) loop := true for loop { - // 是否停止请求执行 + // Check whether the request is exited. if m.request.IsExited() || m.request.handlerIndex >= len(m.request.handlers) { - return + break } item = m.request.handlers[m.request.handlerIndex] m.request.handlerIndex++ - // 中间件执行时不执行钩子函数,由另外的逻辑进行控制 + // Filter the HOOK handlers, which are designed to be called in another standalone procedure. if item.handler.itemType == gHANDLER_TYPE_HOOK { continue } - // 路由参数赋值 + // Router values switching. for k, v := range item.values { m.request.routerMap[k] = v } m.request.Router = item.handler.router - // 执行函数处理 + gutil.TryCatch(func() { switch item.handler.itemType { case gHANDLER_TYPE_CONTROLLER: @@ -98,8 +98,8 @@ func (m *Middleware) Next() { niceCallFunc(func() { item.handler.itemFunc(m.request) }) - // 中间件默认不会进一步执行, - // 需要内部调用Next方法决定是否进一步执行,以便于请求流程控制。 + // It does not continue calling next middleware after another middleware done. + // There should be a "Next" function to be called in the middleware in order to manage the workflow. loop = false } }, func(exception interface{}) { @@ -107,4 +107,12 @@ func (m *Middleware) Next() { m.request.Response.WriteStatus(http.StatusInternalServerError, exception) }) } + // Handle the http status code after all handler and middleware done. + if m.request.Response.Status == 0 { + if m.request.Middleware.served || m.request.Response.buffer.Len() > 0 { + m.request.Response.Status = http.StatusOK + } else { + m.request.Response.WriteStatus(http.StatusNotFound) + } + } } diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index 0c8baf515..21c05e97e 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -20,8 +20,8 @@ import ( "github.com/gogf/gf/util/gconv" ) -// 服务端请求返回对象。 -// 注意该对象并没有实现http.ResponseWriter接口,而是依靠ghttp.ResponseWriter实现。 +// Response is the writer for response buffer. +// Note that it implements the http.ResponseWriter interface with buffering feature. type Response struct { *ResponseWriter // Underlying ResponseWriter. Server *Server // Parent server. @@ -29,7 +29,7 @@ type Response struct { Request *Request // According request. } -// 创建一个ghttp.Response对象指针 +// newResponse creates and returns a new Response object. func newResponse(s *Server, w http.ResponseWriter) *Response { r := &Response{ Server: s, @@ -42,7 +42,7 @@ func newResponse(s *Server, w http.ResponseWriter) *Response { return r } -// 返回信息,任何变量自动转换为bytes +// Write writes to the response buffer. func (r *Response) Write(content ...interface{}) { if len(content) == 0 { return @@ -62,27 +62,32 @@ func (r *Response) Write(content ...interface{}) { } } -// 返回信息,支持自定义format格式 +// WriteOver overwrites the response buffer with . +func (r *Response) WriteOver(content ...interface{}) { + r.ClearBuffer() + r.Write(content...) +} + +// Writef writes the response with fmt.Sprintf. func (r *Response) Writef(format string, params ...interface{}) { r.Write(fmt.Sprintf(format, params...)) } -// 返回信息,末尾增加换行标识符"\n" +// Writef writes the response with and new line. func (r *Response) Writeln(content ...interface{}) { if len(content) == 0 { r.Write("\n") return } - content = append(content, "\n") - r.Write(content...) + r.Write(append(content, "\n")...) } -// 返回信息,末尾增加换行标识符"\n" +// Writefln writes the response with fmt.Sprintf and new line. func (r *Response) Writefln(format string, params ...interface{}) { r.Writeln(fmt.Sprintf(format, params...)) } -// 返回JSON +// WriteJson writes to the response with JSON format. func (r *Response) WriteJson(content interface{}) error { if b, err := json.Marshal(content); err != nil { return err @@ -93,7 +98,8 @@ func (r *Response) WriteJson(content interface{}) error { return nil } -// 返回JSONP +// WriteJson writes to the response with JSONP format. +// Note that there should be a "callback" parameter in the request for JSONP format. func (r *Response) WriteJsonP(content interface{}) error { if b, err := json.Marshal(content); err != nil { return err @@ -112,7 +118,7 @@ func (r *Response) WriteJsonP(content interface{}) error { return nil } -// 返回XML +// WriteJson writes to the response with XML format. func (r *Response) WriteXml(content interface{}, rootTag ...string) error { if b, err := gparser.VarToXml(content, rootTag...); err != nil { return err @@ -123,14 +129,14 @@ func (r *Response) WriteXml(content interface{}, rootTag ...string) error { return nil } -// 返回HTTP Code状态码 +// WriteStatus writes HTTP and to the response. func (r *Response) WriteStatus(status int, content ...interface{}) { // Avoid error: http: multiple response.WriteHeader calls. if r.Status == 0 { r.WriteHeader(status) } if r.buffer.Len() == 0 { - // 状态码注册回调函数处理 + // HTTP status handler. if status != http.StatusOK { if f := r.Request.Server.getStatusHandler(status, r.Request); f != nil { // Call custom status code handler. @@ -152,7 +158,7 @@ func (r *Response) WriteStatus(status int, content ...interface{}) { } } -// 静态文件处理 +// ServeFile serves the file to the response. func (r *Response) ServeFile(path string, allowIndex ...bool) { serveFile := (*staticServeFile)(nil) if file := gres.Get(path); file != nil { @@ -171,7 +177,7 @@ func (r *Response) ServeFile(path string, allowIndex ...bool) { r.Server.serveFile(r.Request, serveFile, allowIndex...) } -// 静态文件下载处理 +// ServeFileDownload serves file as file downloading to the response. func (r *Response) ServeFileDownload(path string, name ...string) { serveFile := (*staticServeFile)(nil) downloadName := "" @@ -203,46 +209,45 @@ func (r *Response) ServeFileDownload(path string, name ...string) { r.Server.serveFile(r.Request, serveFile) } -// 返回location标识,引导客户端跳转。 -// 注意这里要先把设置的cookie输出,否则会被忽略。 +// RedirectTo redirects client to another location. func (r *Response) RedirectTo(location string) { r.Header().Set("Location", location) r.WriteHeader(http.StatusFound) r.Request.Exit() } -// 返回location标识,引导客户端跳转到来源页面 +// RedirectBack redirects client back to referer. func (r *Response) RedirectBack() { r.RedirectTo(r.Request.GetReferer()) } -// 获取当前缓冲区中的数据 +// BufferString returns the buffer content as []byte. func (r *Response) Buffer() []byte { return r.buffer.Bytes() } -// 获取当前缓冲区中的数据(string) +// BufferString returns the buffer content as string. func (r *Response) BufferString() string { return r.buffer.String() } -// 获取当前缓冲区中的数据大小 +// BufferLength returns the length of the buffer content. func (r *Response) BufferLength() int { return r.buffer.Len() } -// 手动设置缓冲区内容 +// SetBuffer overwrites the buffer with . func (r *Response) SetBuffer(data []byte) { r.buffer.Reset() r.buffer.Write(data) } -// 清空缓冲区内容 +// ClearBuffer clears the response buffer. func (r *Response) ClearBuffer() { r.buffer.Reset() } -// 输出缓冲区数据到客户端. +// Output outputs the buffer content to the client. func (r *Response) Output() { r.Header().Set("Server", r.Server.config.ServerAgent) //r.handleGzip() diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index eab8704b5..de5a9795d 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -138,7 +138,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { s.callHookHandler(HOOK_BEFORE_OUTPUT, request) } - // 状态码处理:如果没有产生异常状态,那么设置返回状态为200 + // 返回状态码处理 if request.Response.Status == 0 { if request.Middleware.served || request.Response.buffer.Len() > 0 { request.Response.Status = http.StatusOK diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go new file mode 100644 index 000000000..c155c8c1e --- /dev/null +++ b/net/ghttp/ghttp_unit_client_test.go @@ -0,0 +1,42 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "fmt" + "testing" + "time" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +func Test_Client_Basic(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.BindHandler("/hello", func(r *ghttp.Request) { + r.Response.Write("hello") + }) + s.SetPort(p) + s.SetDumpRouteMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + url := fmt.Sprintf("http://127.0.0.1:%d", p) + client := ghttp.NewClient() + client.SetPrefix(url) + + gtest.Assert(ghttp.GetContent(""), ``) + gtest.Assert(client.GetContent("/hello"), `hello`) + + _, err := ghttp.Post("") + gtest.AssertNE(err, nil) + }) +} diff --git a/net/ghttp/ghttp_unit_middleware_test.go b/net/ghttp/ghttp_unit_middleware_test.go index 740778ad3..c7c10095f 100644 --- a/net/ghttp/ghttp_unit_middleware_test.go +++ b/net/ghttp/ghttp_unit_middleware_test.go @@ -166,6 +166,37 @@ func Test_Middleware_With_Static(t *testing.T) { }) } +func Test_Middleware_Status(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.Group("/", func(g *ghttp.RouterGroup) { + g.Middleware(func(r *ghttp.Request) { + r.Middleware.Next() + r.Response.WriteOver(r.Response.Status) + }) + g.ALL("/user/list", func(r *ghttp.Request) { + r.Response.Write("list") + }) + }) + s.SetPort(p) + s.SetDumpRouteMap(false) + s.Start() + defer s.Shutdown() + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + gtest.Assert(client.GetContent("/"), "404") + gtest.Assert(client.GetContent("/user/list"), "200") + + resp, err := client.Get("/") + defer resp.Close() + gtest.Assert(err, nil) + gtest.Assert(resp.StatusCode, 404) + }) +} + func Test_Middleware_Hook_With_Static(t *testing.T) { p := ports.PopRand() s := g.Server(p)