Skip to content

Commit

Permalink
update context responder
Browse files Browse the repository at this point in the history
  • Loading branch information
xgfone committed May 12, 2024
1 parent 6775914 commit 544d699
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 54 deletions.
46 changes: 41 additions & 5 deletions http/reqresp/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,14 +576,50 @@ func (c *Context) Respond(response result.Response) {
}

var (
// DefaultContextResponder is default result responder based on Context.
DefaultContextResponder func(*Context, result.Response) = RespondResultResponseWithContext

// DefaultResponder is the default result responder.
DefaultResponder func(http.ResponseWriter, *http.Request, result.Response) = RespondResultResponse
DefaultResponder = defaultResponder

// DefaultContextResponder is the default result responder based on Context.
DefaultContextResponder = defaultContextResponder

// DefaultContextResponderForCode is the default result responder
// based on Context and StatusCode.
DefaultContextResponderForStatusCode = defaultContextResponderForStatusCode
)

func RespondResultResponse(w http.ResponseWriter, r *http.Request, response result.Response) {
// XResponseStatusCode is used to save the response status code
// in the request header or query.
var XResponseStatusCode = "X-Response-Status-Code"

func getXStatusCode(c *Context, key string) (xcode string) {
xcode = c.Request.Header.Get(key)
if xcode == "" {
xcode = c.GetQuery(key)
}
return
}

func defaultContextResponder(c *Context, response result.Response) {
var xcode string
if c.Request != nil {
xcode = getXStatusCode(c, XResponseStatusCode)
if xcode == "" && XResponseStatusCode != "X-Response-Code" {
// Compatible with the old
xcode = getXStatusCode(c, "X-Response-Code")
}
}
DefaultContextResponderForStatusCode(c, xcode, response)
}

func defaultContextResponderForStatusCode(c *Context, xcode string, response result.Response) {
if response.Error == nil {
c.JSON(200, response.Data)
} else {
RespondErrorWithContextByCode(c, xcode, response.Error)
}
}

func defaultResponder(w http.ResponseWriter, r *http.Request, response result.Response) {
if c := GetContext(r.Context()); c != nil {
DefaultContextResponder(c, response)
return
Expand Down
104 changes: 55 additions & 49 deletions http/reqresp/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package reqresp
import (
"encoding/json"
"net/http"
"strconv"

"github.com/xgfone/go-apiserver/result"
"github.com/xgfone/go-apiserver/result/codeint"
Expand Down Expand Up @@ -56,76 +57,81 @@ func runhandler(w http.ResponseWriter, r *http.Request, f Handler) {

/// ----------------------------------------------------------------------- ///

func RespondResultResponseWithContext(c *Context, response result.Response) {
if c.Request == nil {
respondstd(c, response)
return
}

xcode := c.Request.Header.Get("X-Response-Code")
if xcode == "" {
xcode = c.GetQuery("X-Response-Code")
}

// RespondErrorWithContextByCode parses xcode as the status code, which supports
//
// - ""
// - "std"
// - number
//
// If xcode is empty, it is equal to "std".
// If xcode is a number and in [100, 599], parse it as status code.
// For other numbers or characters, but, they are equal to "std".
//
// For "std", it will guess the status code from the error.
func RespondErrorWithContextByCode(c *Context, xcode string, err error) {
switch xcode {
case "", "std":
respondstd(c, response)
RespondErrorWithContextAndStatusCode(c, 0, err)

case "200":
respond200(c, response)
RespondErrorWithContextAndStatusCode(c, 200, err)

case "400":
RespondErrorWithContextAndStatusCode(c, 400, err)

case "500":
respond500(c, response)
RespondErrorWithContextAndStatusCode(c, 500, err)

default:
respondstd(c, response)
code, _ := strconv.ParseInt(xcode, 10, 16)
if code >= 600 || code < 0 {
code = 0
}

RespondErrorWithContextAndStatusCode(c, int(code), err)
}
}

func getStatusCodeFromError(err error) int {
if e, ok := err.(StatusCoder); ok {
return e.StatusCode()
// If statuscode is equal to 0, guess it from the error.
func RespondErrorWithContextAndStatusCode(c *Context, statuscode int, err error) {
if statuscode == 0 {
responderrorstd(c, err)
} else {
responderror(c, statuscode, err)
}
return 500
}

func respondstd(c *Context, r result.Response) {
switch e := r.Error.(type) {
case nil:
c.JSON(200, r.Data)

func responderror(c *Context, statuscode int, err error) {
switch err.(type) {
case codeint.Error, json.Marshaler:
c.JSON(getStatusCodeFromError(r.Error), r.Error)

case StatusCoder:
r.Error = codeint.ErrInternalServerError.WithError(r.Error)
c.JSON(e.StatusCode(), r.Error)

default:
r.Error = codeint.ErrInternalServerError.WithError(r.Error)
c.JSON(getStatusCodeFromError(r.Error), r.Error)
err = codeint.ErrInternalServerError.WithError(err)
}
}

func respond200(c *Context, r result.Response) {
switch r.Error.(type) {
case nil:
c.JSON(200, r.Data)
c.JSON(statuscode, err)
}

func responderrorstd(c *Context, err error) {
var statuscode int
switch e := err.(type) {
case codeint.Error, json.Marshaler:
c.JSON(200, r.Error)
statuscode = getStatusCodeFromError(err)

case StatusCoder:
statuscode = e.StatusCode()
err = codeint.ErrInternalServerError.WithError(err)

default:
c.JSON(200, codeint.ErrInternalServerError.WithError(r.Error))
statuscode = getStatusCodeFromError(err)
err = codeint.ErrInternalServerError.WithError(err)
}
}

func respond500(c *Context, r result.Response) {
switch r.Error.(type) {
case nil:
c.JSON(200, r.Data)

case codeint.Error, json.Marshaler:
c.JSON(500, r.Error)
c.JSON(statuscode, err)
}

default:
c.JSON(500, codeint.ErrInternalServerError.WithError(r.Error))
func getStatusCodeFromError(err error) int {
if e, ok := err.(StatusCoder); ok {
return e.StatusCode()
}
return 500
}

0 comments on commit 544d699

Please sign in to comment.