improve package gcmd/ghttp

This commit is contained in:
John Guo
2022-01-12 19:39:38 +08:00
parent 0f67559995
commit 9486f6e7e9
10 changed files with 102 additions and 47 deletions

View File

@ -32,20 +32,24 @@ func niceCallFunc(f func()) {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
// It's already an error that has stack info.
panic(v)
} else {
// Create a new error with stack info.
// Note that there's a skip pointing the start stacktrace
// of the real error point.
if v, ok := exception.(error); ok {
if gerror.Code(v) != gcode.CodeNil {
panic(v)
} else {
panic(gerror.WrapCodeSkip(gcode.CodeInternalError, 1, v, ""))
}
} else {
panic(gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception))
}
}
// Create a new error with stack info.
// Note that there's a skip pointing the start stacktrace
// of the real error point.
if v, ok := exception.(error); ok {
if gerror.Code(v) != gcode.CodeNil {
panic(v)
} else {
panic(gerror.WrapCodeSkip(
gcode.CodeInternalError, 1, v, "exception recovered",
))
}
} else {
panic(gerror.NewCodeSkipf(
gcode.CodeInternalError, 1, "exception recovered: %+v", exception,
))
}
}
}
}()

View File

@ -53,7 +53,7 @@ type Request struct {
formMap map[string]interface{} // Form parameters map, which is nil if there's no form data from client.
bodyMap map[string]interface{} // Body parameters map, which might be nil if there're no body content.
error error // Current executing error of the request.
exit bool // A bool marking whether current request is exited.
exitAll bool // A bool marking whether current request is exited.
parsedHost string // The parsed host name for current host used by GetHost function.
clientIp string // The parsed client ip for current host used by GetClientIp function.
bodyContent []byte // Request body content.
@ -139,7 +139,7 @@ func (r *Request) Exit() {
// ExitAll exits executing of current and following HTTP handlers.
func (r *Request) ExitAll() {
r.exit = true
r.exitAll = true
panic(exceptionExitAll)
}
@ -150,7 +150,7 @@ func (r *Request) ExitHook() {
// IsExited checks and returns whether current request is exited.
func (r *Request) IsExited() bool {
return r.exit
return r.exitAll
}
// GetHeader retrieves and returns the header value with given `key`.

View File

@ -45,7 +45,7 @@ func Test_Log(t *testing.T) {
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
t.Assert(client.GetContent(ctx, "/hello"), "hello")
t.Assert(client.GetContent(ctx, "/error"), "custom error")
t.Assert(client.GetContent(ctx, "/error"), "exception recovered: custom error")
var (
logPath1 = gfile.Join(logDir, gtime.Now().Format("Y-m-d")+".log")

View File

@ -709,6 +709,6 @@ func Test_Middleware_Panic(t *testing.T) {
client := g.Client()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
t.Assert(client.GetContent(ctx, "/"), "error")
t.Assert(client.GetContent(ctx, "/"), "exception recovered: error")
})
}

View File

@ -9,12 +9,13 @@ package gbuild
import (
"context"
"runtime"
"github.com/gogf/gf/v2"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/encoding/gbase64"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/internal/json"
"runtime"
)
var (

View File

@ -204,7 +204,12 @@ type printLineBriefInput struct {
}
func (c *Command) printLineBrief(in printLineBriefInput) {
for i, line := range gstr.SplitAndTrim(in.Brief, "\n") {
briefArray := gstr.SplitAndTrim(in.Brief, "\n")
if len(briefArray) == 0 {
// If command brief is empty, it just prints its command name.
briefArray = []string{""}
}
for i, line := range briefArray {
var lineStr string
if i == 0 {
lineStr = fmt.Sprintf(

View File

@ -245,17 +245,6 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co
// =============================================================================================
command.FuncWithValue = func(ctx context.Context, parser *Parser) (out interface{}, err error) {
ctx = context.WithValue(ctx, CtxKeyParser, parser)
defer func() {
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.New(`exception recovered:` + gconv.String(exception))
}
}
}()
var (
data = gconv.Map(parser.GetOptAll())
argIndex = 0

View File

@ -31,9 +31,17 @@ func (c *Command) Run(ctx context.Context) {
func (c *Command) RunWithValue(ctx context.Context) (value interface{}) {
value, err := c.RunWithValueError(ctx)
if err != nil {
if gerror.Code(err) == gcode.CodeNotFound {
var (
code = gerror.Code(err)
detail = code.Detail()
)
if code.Code() == gcode.CodeNotFound.Code() {
fmt.Printf("ERROR: %s\n", gstr.Trim(err.Error()))
c.Print()
if lastCmd, ok := detail.(*Command); ok {
lastCmd.Print()
} else {
c.Print()
}
} else {
fmt.Printf("%+v\n", err)
}
@ -64,23 +72,34 @@ func (c *Command) RunWithValueError(ctx context.Context) (value interface{}, err
args = args[1:]
// Find the matched command and run it.
if subCommand, newCtx := c.searchCommand(ctx, args); subCommand != nil {
return subCommand.doRun(newCtx, parser)
lastCmd, foundCmd, newCtx := c.searchCommand(ctx, args)
if foundCmd != nil {
return foundCmd.doRun(newCtx, parser)
}
// Print error and help command if no command found.
err = gerror.NewCodef(
gcode.CodeNotFound,
`command "%s" not found for arguments "%s"`,
gcode.WithCode(gcode.CodeNotFound, lastCmd),
`command "%s" not found for command "%s", command line: %s`,
gstr.Join(args, " "),
c.Name,
gstr.Join(os.Args, " "),
)
return
}
func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{}, err error) {
ctx = context.WithValue(ctx, CtxKeyCommand, c)
defer func() {
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.Newf(`exception recovered: %+v`, exception)
}
}
}()
ctx = context.WithValue(ctx, CtxKeyCommand, c)
// Check built-in help command.
if parser.ContainsOpt(helpOptionName) || parser.ContainsOpt(helpOptionNameShort) {
if c.HelpFunc != nil {
@ -154,12 +173,11 @@ func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error)
}
// searchCommand recursively searches the command according given arguments.
func (c *Command) searchCommand(ctx context.Context, args []string) (*Command, context.Context) {
func (c *Command) searchCommand(ctx context.Context, args []string) (lastCmd, foundCmd *Command, newCtx context.Context) {
if len(args) == 0 {
return nil, ctx
return c, nil, ctx
}
for _, cmd := range c.commands {
// Recursively searching the command.
if cmd.Name == args[0] {
leftArgs := args[1:]
@ -167,16 +185,16 @@ func (c *Command) searchCommand(ctx context.Context, args []string) (*Command, c
// it then gives all its left arguments to it.
if cmd.hasArgumentFromIndex() {
ctx = context.WithValue(ctx, CtxKeyArguments, leftArgs)
return cmd, ctx
return c, cmd, ctx
}
// Recursively searching.
if len(leftArgs) == 0 {
return cmd, ctx
return c, cmd, ctx
}
return cmd.searchCommand(ctx, leftArgs)
}
}
return nil, ctx
return c, nil, ctx
}
func (c *Command) hasArgumentFromIndex() bool {

View File

@ -11,6 +11,7 @@ package gcmd_test
import (
"context"
"fmt"
"os"
"testing"
"github.com/gogf/gf/v2/errors/gcode"
@ -222,3 +223,35 @@ gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./dock
_ = c.RunWithError(ctx)
})
}
func Test_Command_NotFound(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
c0 := &gcmd.Command{
Name: "c0",
}
c1 := &gcmd.Command{
Name: "c1",
FuncWithValue: func(ctx context.Context, parser *gcmd.Parser) (interface{}, error) {
return nil, nil
},
}
c21 := &gcmd.Command{
Name: "c21",
FuncWithValue: func(ctx context.Context, parser *gcmd.Parser) (interface{}, error) {
return nil, nil
},
}
c22 := &gcmd.Command{
Name: "c22",
FuncWithValue: func(ctx context.Context, parser *gcmd.Parser) (interface{}, error) {
return nil, nil
},
}
t.AssertNil(c0.AddCommand(c1))
t.AssertNil(c1.AddCommand(c21, c22))
os.Args = []string{"c0", "c1", "c23", `--test="abc"`}
err := c0.RunWithError(gctx.New())
t.Assert(err.Error(), `command "c1 c23" not found for command "c0", command line: c0 c1 c23 --test="abc"`)
})
}

View File

@ -10,6 +10,7 @@ import (
"context"
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/errors/gerror"
)
// Entry is the timing job.
@ -45,9 +46,13 @@ func (entry *Entry) Run() {
}
go func() {
defer func() {
if err := recover(); err != nil {
if err != panicExit {
panic(err)
if exception := recover(); exception != nil {
if exception != panicExit {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
panic(v)
} else {
panic(gerror.Newf(`exception recovered: %+v`, exception))
}
} else {
entry.Close()
return