diff --git a/cmd/gf/internal/cmd/cmd_gen_service.go b/cmd/gf/internal/cmd/cmd_gen_service.go index 2de983ec3..e9e265bf8 100644 --- a/cmd/gf/internal/cmd/cmd_gen_service.go +++ b/cmd/gf/internal/cmd/cmd_gen_service.go @@ -23,7 +23,7 @@ type ( SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed. default: internal/logic" d:"internal/logic"` DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files. default: internal/service" d:"internal/service"` WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under srcFolder"` - StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s([A-Z]\\w+)" d:"s([A-Z]\\w+)"` + StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s([A-Z]\\\w+)" d:"s([A-Z]\\w+)"` Packages string `short:"p" name:"packages" brief:"produce go files only for given source packages, multiple packages joined with char ','"` ImportPrefix string `short:"i" name:"importPrefix" brief:"custom import prefix to calculate import path for generated importing go file of logic"` OverWrite bool `short:"o" name:"overwrite" brief:"overwrite service go files that already exist in generating folder. default: true" d:"true" orphan:"true"` diff --git a/contrib/registry/etcd/README.MD b/contrib/registry/etcd/README.MD new file mode 100644 index 000000000..a1cac7693 --- /dev/null +++ b/contrib/registry/etcd/README.MD @@ -0,0 +1,79 @@ +# GoFrame Etcd Registry + + +Use `etcd` as service registration and discovery management. + + +## Installation +``` +go get -u -v github.com/gogf/gf/contrib/registry/etcd/v2 +``` +suggested using `go.mod`: +``` +require github.com/gogf/gf/contrib/registry/etcd/v2 latest +``` + + +## Example + +### Reference example + +[server](example/registry/etcd/server/main.go) +```go +package main + +import ( + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/net/gsvc" +) + +func main() { + gsvc.SetRegistry(etcd.New(`127.0.0.1:2379`)) + + s := g.Server(`hello.svc`) + s.BindHandler("/", func(r *ghttp.Request) { + g.Log().Info(r.Context(), `request received`) + r.Response.Write(`Hello world`) + }) + s.Run() +} +``` + +[client](example/registry/etcd/client/main.go) +```go +package main + +import ( + "fmt" + "time" + + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/gsel" + "github.com/gogf/gf/v2/net/gsvc" + "github.com/gogf/gf/v2/os/gctx" +) + +func main() { + gsvc.SetRegistry(etcd.New(`127.0.0.1:2379`)) + gsel.SetBuilder(gsel.NewBuilderRoundRobin()) + + client := g.Client() + for i := 0; i < 100; i++ { + res, err := client.Get(gctx.New(), `http://hello.svc/`) + if err != nil { + panic(err) + } + fmt.Println(res.ReadAllString()) + res.Close() + time.Sleep(time.Second) + } +} +``` + +## License + +`GoFrame etcd` is licensed under the [MIT License](../../../LICENSE), 100% free and open-source, forever. + diff --git a/contrib/registry/polaris/README.MD b/contrib/registry/polaris/README.MD index 9fd2927ab..c60572b21 100644 --- a/contrib/registry/polaris/README.MD +++ b/contrib/registry/polaris/README.MD @@ -2,7 +2,7 @@ English | [简体中文](README_ZH.MD) -Use Polaris mesh as service registration, discovery management and heartbeat reporting +Use `PolarisMesh` as service registration, discovery management and heartbeat reporting. ## Installation @@ -14,11 +14,6 @@ suggested using `go.mod`: require github.com/gogf/gf/contrib/registry/polaris/v2 latest ``` -## Limitation -``` -golang version >= 1.15 -``` - ## Example @@ -50,7 +45,6 @@ func main() { }) s.Run() } - ``` [client](example/registry/polaris/client/main.go) @@ -85,7 +79,6 @@ func main() { time.Sleep(time.Second) } } - ``` ## License diff --git a/contrib/registry/polaris/README_ZH.MD b/contrib/registry/polaris/README_ZH.MD index 47ab34ced..587da4ead 100644 --- a/contrib/registry/polaris/README_ZH.MD +++ b/contrib/registry/polaris/README_ZH.MD @@ -2,7 +2,7 @@ [English](README.MD) | 简体中文 -使用Polarismesh作为服务注册、发现管理和心跳上报 +使用`PolarisMesh`作为服务注册、发现管理和心跳上报。 ## Installation diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index 95a2494a2..ee3e6c746 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -222,6 +222,9 @@ func Code(err error) gcode.Code { if e, ok := err.(iNext); ok { return Code(e.Next()) } + if e, ok := err.(iUnwrap); ok { + return Code(e.Unwrap()) + } return gcode.CodeNil } @@ -236,6 +239,9 @@ func Cause(err error) error { if e, ok := err.(iNext); ok { return Cause(e.Next()) } + if e, ok := err.(iUnwrap); ok { + return Cause(e.Unwrap()) + } return err } diff --git a/errors/gerror/gerror_interface.go b/errors/gerror/gerror_interface.go index 317f951dc..f4b5a32d7 100644 --- a/errors/gerror/gerror_interface.go +++ b/errors/gerror/gerror_interface.go @@ -49,3 +49,9 @@ type iNext interface { Error() string Next() error } + +// iUnwrap is the interface for Unwrap feature. +type iUnwrap interface { + Error() string + Unwrap() error +} diff --git a/errors/gerror/gerror_z_example_test.go b/errors/gerror/gerror_z_example_test.go index e43338e9c..998b54237 100644 --- a/errors/gerror/gerror_z_example_test.go +++ b/errors/gerror/gerror_z_example_test.go @@ -55,3 +55,30 @@ func ExampleWrapCodef() { // It's Custom Error: permission denied // 10000 } + +func ExampleEqual() { + err1 := errors.New("permission denied") + err2 := gerror.New("permission denied") + err3 := gerror.NewCode(gcode.CodeNotAuthorized, "permission denied") + fmt.Println(gerror.Equal(err1, err2)) + fmt.Println(gerror.Equal(err2, err3)) + + // Output: + // true + // false +} + +func ExampleIs() { + err1 := errors.New("permission denied") + err2 := gerror.Wrap(err1, "operation failed") + fmt.Println(gerror.Is(err1, err1)) + fmt.Println(gerror.Is(err2, err2)) + fmt.Println(gerror.Is(err2, err1)) + fmt.Println(gerror.Is(err1, err2)) + + // Output: + // false + // true + // true + // false +} diff --git a/net/gclient/gclient.go b/net/gclient/gclient.go index 9219fa3b0..70450822c 100644 --- a/net/gclient/gclient.go +++ b/net/gclient/gclient.go @@ -79,13 +79,17 @@ func New() *Client { func (c *Client) Clone() *Client { newClient := New() *newClient = *c - newClient.header = make(map[string]string) - newClient.cookies = make(map[string]string) - for k, v := range c.header { - newClient.header[k] = v + if len(c.header) > 0 { + newClient.header = make(map[string]string) + for k, v := range c.header { + newClient.header[k] = v + } } - for k, v := range c.cookies { - newClient.cookies[k] = v + if len(c.cookies) > 0 { + newClient.cookies = make(map[string]string) + for k, v := range c.cookies { + newClient.cookies[k] = v + } } return newClient } diff --git a/net/gclient/gclient_chain.go b/net/gclient/gclient_chain.go index a24e2c3db..03b046936 100644 --- a/net/gclient/gclient_chain.go +++ b/net/gclient/gclient_chain.go @@ -12,6 +12,9 @@ import ( // Prefix is a chaining function, // which sets the URL prefix for next request of this client. +// Eg: +// Prefix("http://127.0.0.1:8199/api/v1") +// Prefix("http://127.0.0.1:8199/api/v2") func (c *Client) Prefix(prefix string) *Client { newClient := c.Clone() newClient.SetPrefix(prefix) diff --git a/net/gclient/gclient_request_obj.go b/net/gclient/gclient_request_obj.go new file mode 100644 index 000000000..4fa48bd3e --- /dev/null +++ b/net/gclient/gclient_request_obj.go @@ -0,0 +1,95 @@ +// Copyright GoFrame Author(https://goframe.org). 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 gclient + +import ( + "context" + "net/http" + "reflect" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/net/goai" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gmeta" +) + +// DoRequestObj does HTTP request using standard request/response object. +// The request object `req` is defined like: +// type UseCreateReq struct { +// g.Meta `path:"/user" method:"put"` +// // other fields.... +// } +// The response object `res` should be a pointer type. It automatically converts result +// to given object `res` is success. +// Eg: +// var ( +// req = UseCreateReq{} +// res *UseCreateRes +// ) +// DoRequestObj(ctx, req, &res) +func (c *Client) DoRequestObj(ctx context.Context, req, res interface{}) error { + var ( + method = gmeta.Get(req, goai.TagNameMethod).String() + path = gmeta.Get(req, goai.TagNamePath).String() + ) + if method == "" { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `no "%s" tag found in request object: %s`, + goai.TagNameMethod, reflect.TypeOf(req).String(), + ) + } + if path == "" { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `no "%s" tag found in request object: %s`, + goai.TagNamePath, reflect.TypeOf(req).String(), + ) + } + path = c.handlePathForObjRequest(path, req) + switch gstr.ToUpper(method) { + case + http.MethodGet, + http.MethodPut, + http.MethodPost, + http.MethodDelete, + http.MethodHead, + http.MethodPatch, + http.MethodConnect, + http.MethodOptions, + http.MethodTrace: + if result := c.RequestVar(ctx, method, path, req); res != nil && !result.IsEmpty() { + return result.Scan(res) + } + return nil + + default: + return gerror.Newf(`invalid HTTP method "%s"`, method) + } +} + +// handlePathForObjRequest replaces parameters in `path` with parameters from request object. +// Eg: +// /order/{id} -> /order/1 +// /user/{name} -> /order/john +func (c *Client) handlePathForObjRequest(path string, req interface{}) string { + if gstr.Contains(path, "{") { + requestParamsMap := gconv.MapStrStr(req) + if len(requestParamsMap) > 0 { + path, _ = gregex.ReplaceStringFuncMatch(`\{(\w+)\}`, path, func(match []string) string { + if v, ok := requestParamsMap[match[1]]; ok { + return v + } + return match[1] + }) + } + } + return path +} diff --git a/net/gclient/gclient_z_example_test.go b/net/gclient/gclient_z_example_test.go index 24d4034d1..00bb657cc 100644 --- a/net/gclient/gclient_z_example_test.go +++ b/net/gclient/gclient_z_example_test.go @@ -11,13 +11,14 @@ import ( "crypto/tls" "encoding/hex" "fmt" + "net/http" + "time" + "github.com/gogf/gf/v2/debug/gdebug" "github.com/gogf/gf/v2/net/gclient" "github.com/gogf/gf/v2/net/gtcp" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gfile" - "net/http" - "time" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" diff --git a/net/gclient/gclient_z_unit_feature_trace_test.go b/net/gclient/gclient_z_unit_feature_trace_test.go new file mode 100644 index 000000000..e01ca4cd1 --- /dev/null +++ b/net/gclient/gclient_z_unit_feature_trace_test.go @@ -0,0 +1,78 @@ +// Copyright GoFrame Author(https://goframe.org). 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 gclient_test + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/internal/tracing" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/net/gtcp" + "github.com/gogf/gf/v2/test/gtest" + "go.opentelemetry.io/otel" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/trace" +) + +type CustomProvider struct { + *sdkTrace.TracerProvider +} + +func NewCustomProvider() *CustomProvider { + return &CustomProvider{ + TracerProvider: sdkTrace.NewTracerProvider( + sdkTrace.WithIDGenerator(NewCustomIDGenerator()), + ), + } +} + +type CustomIDGenerator struct{} + +func NewCustomIDGenerator() *CustomIDGenerator { + return &CustomIDGenerator{} +} + +func (id *CustomIDGenerator) NewIDs(ctx context.Context) (traceID trace.TraceID, spanID trace.SpanID) { + return tracing.NewIDs() +} + +func (id *CustomIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) (spanID trace.SpanID) { + return tracing.NewSpanID() +} + +func TestClient_CustomProvider(t *testing.T) { + provider := otel.GetTracerProvider() + defer otel.SetTracerProvider(provider) + + otel.SetTracerProvider(NewCustomProvider()) + + p, _ := gtcp.GetFreePort() + s := g.Server(p) + s.BindHandler("/hello", func(r *ghttp.Request) { + r.Response.WriteHeader(200) + r.Response.WriteJson(g.Map{"field": "test_for_response_body"}) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + c := g.Client() + url := fmt.Sprintf("127.0.0.1:%d/hello", p) + resp, err := c.DoRequest(ctx, http.MethodGet, url) + t.AssertNil(err) + t.AssertNE(resp, nil) + t.Assert(resp.ReadAllString(), "{\"field\":\"test_for_response_body\"}") + }) +} diff --git a/net/gclient/gclient_z_unit_request_obj_test.go b/net/gclient/gclient_z_unit_request_obj_test.go new file mode 100644 index 000000000..7c9d2da0e --- /dev/null +++ b/net/gclient/gclient_z_unit_request_obj_test.go @@ -0,0 +1,78 @@ +// Copyright GoFrame Author(https://goframe.org). 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 gclient_test + +import ( + "fmt" + "testing" + "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/net/gtcp" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_Client_DoRequestObj(t *testing.T) { + type UserCreateReq struct { + g.Meta `path:"/user" method:"post"` + Id int + Name string + } + type UserCreateRes struct { + Id int + } + type UserQueryReq struct { + g.Meta `path:"/user" method:"get"` + } + type UserQueryRes struct { + Id int + Name string + } + p, _ := gtcp.GetFreePort() + s := g.Server(p) + s.Group("/user", func(group *ghttp.RouterGroup) { + group.GET("/", func(r *ghttp.Request) { + r.Response.WriteJson(g.Map{"id": 1, "name": "john"}) + }) + group.POST("/", func(r *ghttp.Request) { + r.Response.WriteJson(g.Map{"id": r.Get("Id")}) + }) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + url := fmt.Sprintf("http://127.0.0.1:%d", p) + client := g.Client().SetPrefix(url).ContentJson() + var ( + createRes *UserCreateRes + createReq = UserCreateReq{ + Id: 1, + Name: "john", + } + ) + err := client.DoRequestObj(ctx, createReq, &createRes) + t.AssertNil(err) + t.Assert(createRes.Id, 1) + }) + gtest.C(t, func(t *gtest.T) { + url := fmt.Sprintf("http://127.0.0.1:%d", p) + client := g.Client().SetPrefix(url).ContentJson() + var ( + queryRes *UserQueryRes + queryReq = UserQueryReq{} + ) + err := client.DoRequestObj(ctx, queryReq, &queryRes) + t.AssertNil(err) + t.Assert(queryRes.Id, 1) + t.Assert(queryRes.Name, "john") + }) +} diff --git a/net/gclient/gclient_z_unit_test.go b/net/gclient/gclient_z_unit_test.go index 806be5f6d..410645449 100644 --- a/net/gclient/gclient_z_unit_test.go +++ b/net/gclient/gclient_z_unit_test.go @@ -11,16 +11,13 @@ import ( "context" "crypto/tls" "fmt" - "github.com/gogf/gf/v2/debug/gdebug" - "github.com/gogf/gf/v2/internal/tracing" - "go.opentelemetry.io/otel" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" "testing" "time" + "github.com/gogf/gf/v2/debug/gdebug" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/gclient" @@ -643,58 +640,3 @@ func TestClient_RequestVar(t *testing.T) { t.AssertNE(users, nil) }) } - -type CustomProvider struct { - *sdkTrace.TracerProvider -} - -func NewCustomProvider() *CustomProvider { - return &CustomProvider{ - TracerProvider: sdkTrace.NewTracerProvider( - sdkTrace.WithIDGenerator(NewCustomIDGenerator()), - ), - } -} - -type CustomIDGenerator struct{} - -func NewCustomIDGenerator() *CustomIDGenerator { - return &CustomIDGenerator{} -} - -func (id *CustomIDGenerator) NewIDs(ctx context.Context) (traceID trace.TraceID, spanID trace.SpanID) { - return tracing.NewIDs() -} - -func (id *CustomIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) (spanID trace.SpanID) { - return tracing.NewSpanID() -} - -func TestClient_CustomProvider(t *testing.T) { - provider := otel.GetTracerProvider() - defer otel.SetTracerProvider(provider) - - otel.SetTracerProvider(NewCustomProvider()) - - p, _ := gtcp.GetFreePort() - s := g.Server(p) - s.BindHandler("/hello", func(r *ghttp.Request) { - r.Response.WriteHeader(200) - r.Response.WriteJson(g.Map{"field": "test_for_response_body"}) - }) - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - c := g.Client() - url := fmt.Sprintf("127.0.0.1:%d/hello", p) - resp, err := c.DoRequest(ctx, http.MethodGet, url) - t.AssertNil(err) - t.AssertNE(resp, nil) - t.Assert(resp.ReadAllString(), "{\"field\":\"test_for_response_body\"}") - }) - -} diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go index 8fbb9b705..554996b78 100644 --- a/net/ghttp/ghttp_middleware_handler_response.go +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -11,7 +11,6 @@ import ( "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/internal/intlog" ) // DefaultHandlerResponse is the default implementation of HandlerResponse. @@ -32,7 +31,6 @@ func MiddlewareHandlerResponse(r *Request) { var ( msg string - ctx = r.Context() err = r.GetError() res = r.GetHandlerResponse() code = gerror.Code(err) @@ -55,12 +53,9 @@ func MiddlewareHandlerResponse(r *Request) { } else { code = gcode.CodeOK } - internalErr := r.Response.WriteJson(DefaultHandlerResponse{ + r.Response.WriteJson(DefaultHandlerResponse{ Code: code.Code(), Message: msg, Data: res, }) - if internalErr != nil { - intlog.Errorf(ctx, `%+v`, internalErr) - } } diff --git a/net/ghttp/ghttp_response_write.go b/net/ghttp/ghttp_response_write.go index 34878cd5d..9d9565ec8 100644 --- a/net/ghttp/ghttp_response_write.go +++ b/net/ghttp/ghttp_response_write.go @@ -12,6 +12,7 @@ import ( "net/http" "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/internal/json" "github.com/gogf/gf/v2/util/gconv" ) @@ -102,48 +103,42 @@ func (r *Response) WriteflnExit(format string, params ...interface{}) { } // WriteJson writes `content` to the response with JSON format. -func (r *Response) WriteJson(content interface{}) error { +func (r *Response) WriteJson(content interface{}) { r.Header().Set("Content-Type", contentTypeJson) // If given string/[]byte, response it directly to the client. switch content.(type) { case string, []byte: r.Write(gconv.String(content)) - return nil } // Else use json.Marshal function to encode the parameter. if b, err := json.Marshal(content); err != nil { - return err + panic(gerror.Wrap(err, `WriteJson failed`)) } else { r.Write(b) } - return nil } // WriteJsonExit writes `content` to the response with JSON format and exits executing // of current handler if success. The "Exit" feature is commonly used to replace usage of // return statements in the handler, for convenience. -func (r *Response) WriteJsonExit(content interface{}) error { - if err := r.WriteJson(content); err != nil { - return err - } +func (r *Response) WriteJsonExit(content interface{}) { + r.WriteJson(content) r.Request.Exit() - return nil } // WriteJsonP writes `content` 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 { +func (r *Response) WriteJsonP(content interface{}) { r.Header().Set("Content-Type", contentTypeJson) // If given string/[]byte, response it directly to client. switch content.(type) { case string, []byte: r.Write(gconv.String(content)) - return nil } // Else use json.Marshal function to encode the parameter. if b, err := json.Marshal(content); err != nil { - return err + panic(gerror.Wrap(err, `WriteJsonP failed`)) } else { // r.Header().Set("Content-Type", "application/json") if callback := r.Request.Get("callback").String(); callback != "" { @@ -156,7 +151,6 @@ func (r *Response) WriteJsonP(content interface{}) error { r.Write(b) } } - return nil } // WriteJsonPExit writes `content` to the response with JSONP format and exits executing @@ -164,40 +158,32 @@ func (r *Response) WriteJsonP(content interface{}) error { // return statements in the handler, for convenience. // // Note that there should be a "callback" parameter in the request for JSONP format. -func (r *Response) WriteJsonPExit(content interface{}) error { - if err := r.WriteJsonP(content); err != nil { - return err - } +func (r *Response) WriteJsonPExit(content interface{}) { + r.WriteJsonP(content) r.Request.Exit() - return nil } // WriteXml writes `content` to the response with XML format. -func (r *Response) WriteXml(content interface{}, rootTag ...string) error { +func (r *Response) WriteXml(content interface{}, rootTag ...string) { r.Header().Set("Content-Type", contentTypeXml) // If given string/[]byte, response it directly to clients. switch content.(type) { case string, []byte: r.Write(gconv.String(content)) - return nil } if b, err := gjson.New(content).ToXml(rootTag...); err != nil { - return err + panic(gerror.Wrap(err, `WriteXml failed`)) } else { r.Write(b) } - return nil } // WriteXmlExit writes `content` to the response with XML format and exits executing // of current handler if success. The "Exit" feature is commonly used to replace usage // of return statements in the handler, for convenience. -func (r *Response) WriteXmlExit(content interface{}, rootTag ...string) error { - if err := r.WriteXml(content, rootTag...); err != nil { - return err - } +func (r *Response) WriteXmlExit(content interface{}, rootTag ...string) { + r.WriteXml(content, rootTag...) r.Request.Exit() - return nil } // WriteStatus writes HTTP `status` and `content` to the response. diff --git a/net/ghttp/ghttp_server_openapi.go b/net/ghttp/ghttp_server_openapi.go index 72fd10644..0ca79a390 100644 --- a/net/ghttp/ghttp_server_openapi.go +++ b/net/ghttp/ghttp_server_openapi.go @@ -9,7 +9,6 @@ package ghttp import ( "context" - "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/net/goai" "github.com/gogf/gf/v2/text/gstr" ) @@ -48,16 +47,9 @@ func (s *Server) initOpenApi() { // openapiSpec is a build-in handler automatic producing for openapi specification json file. func (s *Server) openapiSpec(r *Request) { - var ( - err error - ) if s.config.OpenApiPath == "" { r.Response.Write(`OpenApi specification file producing is disabled`) } else { - err = r.Response.WriteJson(s.openapi) - } - - if err != nil { - intlog.Errorf(r.Context(), `%+v`, err) + r.Response.WriteJson(s.openapi) } }