I have problem in Golang echo framwork Error response like this.
{ERROR Response json struct}
{"code":-208,"message":"Fail to login","internal":"{ERROR Message}","title":null,"btn_text":null} {"code":-208,"message":"Fail to login","internal":"{ERROR Message}","title":null,"btn_text":null}
Whenever I use BodyDump, duplicate error responses are returned, but I can't find the reason.
Below is the source code of the body dump. I commented out all the actual use parts, but simply applying the body dump returns redundant json body data.
func MWBodyDumpForELS() echo.MiddlewareFunc {
config := middleware.BodyDumpConfig{
Skipper: middleware.DefaultSkipper,
Handler: func(c echo.Context, reqBody, resBody []byte) {
//session, _ := c.Get(SESSION_KEY).(*service.Session)
//go els.Send(c.Path(), session.UserID, session.MembershipType, c.RealIP(), c.Request().RequestURI,
// c.Request().UserAgent(), c.Request().Method, c.Response().Header().Get("Content-Type"),
// c.QueryParams(), c.Response().Status, reqBody, resBody)
return
},
}
return middleware.BodyDumpWithConfig(config)
}
The HTTPErrorHandler I defined is It only seems to happen whenever it returns to that case.:
func HTTPErrorHandler(err error, c echo.Context) {
// code := http.StatusInternalServerError
switch t := err.(type) {
case *echo.HTTPError:
c.JSON(t.Code, NewAPIError(t.Code, fmt.Sprint(t.Message)).internal(t.Internal))
case *APIError:
c.JSON(t.httpCode, t)
default:
if err == nil {
c.JSON(http.StatusInternalServerError, ErrUnknown)
} else {
c.JSON(http.StatusInternalServerError, ErrUnknown.internal(err.Error()))
}
}
}
Someone know anything about this?
Related
package main
import (
"errors"
"fmt"
)
type myError struct{ err error }
func (e myError) Error() string { return e.err.Error() }
func new(msg string, args ...any) error {
return myError{fmt.Errorf(msg, args...)}
}
func (e myError) Unwrap() error { return e.err }
func (e myError) As(target any) bool { return errors.As(e.err, target) }
func (e myError) Is(target error) bool { return errors.Is(e.err, target) }
func isMyError(err error) bool {
target := new("")
return errors.Is(err, target)
}
func asMyError(err error) (error, bool) {
var target myError
ok := errors.As(err, &target)
return target, ok
}
func main() {
err := fmt.Errorf("wrap: %w", new("I am a myError"))
fmt.Println(isMyError(err))
fmt.Println(asMyError(err))
err = fmt.Errorf("wrap: %w", errors.New("I am not a myError"))
fmt.Println(isMyError(err))
fmt.Println(asMyError(err))
}
I expected
true
I am a myError true
false
I am not a myError false
but I got
false
I am a myError true
false
%!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference) false
I tried to add
func (e myError) Unwrap() error { return e.err }
func (e myError) As(target any) bool { return errors.As(e.err, target) }
func (e myError) Is(target error) bool { return errors.Is(e.err, target) }
I tried
func asMyError(err error) (error, bool) {
target := &myError{} // was 'var target myError' before
ok := errors.As(err, &target)
return target, ok
}
I tried
func new(msg string, args ...any) error {
return &myError{fmt.Errorf(msg, args...)} // The change is the character '&'
}
but none of these changed anything. I also tried
func asMyError(err error) (error, bool) {
target := new("") // // was 'var target myError' or 'target := &myError{}' before
ok := errors.As(err, &target)
return target, ok
}
and then I got
false
wrap: I am a myError true
false
wrap: I am not a myError true
, which I guess makes sense but again does not solve my problem. I have a hard time to wrap my head this problem. Can you give me a hand?
So the point of errors.Is and errors.As is that errors can be wrapped, and these functions allow you to extract what the underlying cause of a given error was. This essentially relies on certain errors having specific error values. Take this as an example:
const (
ConnectionFailed = "connection error"
ConnectionTimedOut = "connection timed out"
)
type PkgErr struct {
Msg string
}
func (p PkgErr) Error() string {
return p.Msg
}
func getError(msg string) error {
return PkgErr{
Msg: msg,
}
}
func DoStuff() (bool, error) {
// stuff
err := getError(ConnectionFailed)
return false, fmt.Errorf("unable to do stuff: %w", err)
}
Then, in the caller, you can do something like this:
_, err := pkg.DoStuff()
var pErr pkg.PkgErr
if errors.As(err, &pErr) {
fmt.Printf("DoStuff failed with error %s, underlying error is: %s\n", err, pErr)
}
Or, if you only want to handle connection timeouts, but connection errors should instantly fail, you could do something like this:
accept := pkg.PkgErr{
Msg: pkg.ConnectionTimedOut,
}
if err := pkg.DoStuff(); err != nil {
if !errors.Is(err, accept) {
panic("Fatal: " + err.Error())
}
// handle timeout
}
There is, essentially, nothing you need to implement for the unwrap/is/as part. The idea is that you get a "generic" error back, and you want to unwrap the underlying error values that you know about, and you can/want to handle. If anything, at this point, the custom error type is more of a nuisance than an added value. The common way of using this wrapping/errors.Is thing is by just having your errors as variables:
var (
ErrConnectionFailed = errors.New("connection error")
ErrConnectionTimedOut = errors.New("connection timed out")
)
// then return something like this:
return fmt.Errorf("failed to do X: %w", ErrConnectionFailed)
Then in the caller, you can determine why something went wrong by doing:
if error.Is(err, pkg.ErrConnectionFailed) {
panic("connection is borked")
} else if error.Is(err, pkg.ErrConnectionTimedOut) {
// handle connection time-out, perhaps retry...
}
An example of how this is used can be found in the SQL packages. The driver package has an error variable defined like driver.ErrBadCon, but errors from DB connections can come from various places, so when interacting with a resource like this, you can quickly work out what went wrong by doing something like:
if err := foo.DoStuff(); err != nil {
if errors.Is(err, driver.ErrBadCon) {
panic("bad connection")
}
}
I myself haven't really used the errors.As all that much. IMO, it feels a bit wrong to return an error, and pass it further up the call stack to be handled depending on what the error exactly is, or even: extract an underlying error (often removing data), to pass it back up. I suppose it could be used in cases where error messages could contain sensitive information you don't want to send back to a client or something:
// dealing with credentials:
var ErrInvalidData = errors.New("data invalid")
type SanitizedErr struct {
e error
}
func (s SanitizedErr) Error() string { return s.e.Error() }
func Authenticate(user, pass string) error {
// do stuff
if !valid {
return fmt.Errorf("user %s, pass %s invalid: %w", user, pass, SanitizedErr{
e: ErrInvalidData,
})
}
}
Now, if this function returns an error, to prevent the user/pass data to be logged or sent back in any way shape or form, you can extract the generic error message by doing this:
var sanitized pkg.SanitizedErr
_ = errors.As(err, &sanitized)
// return error
return sanitized
All in all though, this has been a part of the language for quite some time, and I've not seen it used all that much. If you want your custom error types to implement an Unwrap function of sorts, though, the way to do this is really quite easy. Taking this sanitized error type as an example:
func (s SanitizedErr) Unwrap() error {
return s.e
}
That's all. The thing to keep in mind is that, at first glance, the Is and As functions work recursively, so the more custom types that you use that implement this Unwrap function, the longer the actual unwrapping will take. That's not even accounting for situations where you might end up with something like this:
boom := SanitizedErr{}
boom.e = boom
Now the Unwrap method will simply return the same error over and over again, which is just a recipe for disaster. The value you get from this is, IMO, quite minimal anyway.
I am using a helper function to decode JSON. It returns a custom error type that is populated with the reason why it could not parse the JSON and the HTTP code I should return.
package dto
type MalformedRequestError struct {
Status int
Message string
}
func (mr *MalformedRequestError) Error() string {
return mr.Message
}
One of the first things I do when decoding the body is to check that the client has correctly set the Content-Type header.
package webhandlers
func decodeJSONBody(w http.ResponseWriter, r *http.Request, dst interface{}) error {
if r.Header.Get("Content-Type") != "" {
value, _ := header.ParseValueAndParams(r.Header, "Content-Type")
if value != "application/json" {
Message := "Content-Type header is not application/json"
return &dto.MalformedRequestError{Status: http.StatusUnsupportedMediaType, Message: Message}
}
}
... etc ...
I try to use errors.As() to check that the function is returning my custom error, but it is not working.
package webhandlers
func (i *InternalTokenHandler) Post(w http.ResponseWriter, r *http.Request) {
type postRequest struct {
Google_id_token string
}
// parse request data
var parsedRequest postRequest
err := decodeJSONBody(w, r, &parsedRequest)
if err != nil {
// outputs *dto.MalformedRequestError
fmt.Println(fmt.Sprintf("%T", err))
var mr *dto.MalformedRequestError
if errors.As(err, &mr) {
http.Error(w, mr.Message, mr.Status)
} else {
log.Println(err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
.... more code ...
I have checked that the type of the error is *dto.MalformedRequestError, but my code is always reaching the else block and returning the generic server 500 error.
What am I missing - why is errors.As() not recognizing the error type?
This works: https://go.dev/play/p/CWe9mVp7QOz.
The only reason I can think of that would cause your code to fail, if it really does fail, is that the dto package used by decodeJSONBody is not the same as the dto package used by InternalTokenHandler.Post, hence the two error types would be different.
Note that even different versions of the same package count as different packages and types declared in those are not identical.
I have a function with this signature in Go:
func GetAccount(ctx context.Context, id uuid.UUID) (*Account, error)
It returns an error if there's an internal error (like the database query fails for some reason), but I'm not sure what I should return if the account is not found. I can think of two different approaches:
Just return a nil account and nil error if no account is found
Return a custom error type like this:
type accountNotFoundErr struct {
id uuid.UUID
}
func (err accountNotFoundErr) Error() string {
return fmt.Sprintf("account not found for user: %v", err.id)
}
func IsAccountNotFoundErr(err error) bool {
_, ok := err.(accountNotFoundErr)
return ok
}
func GetAccount(ctx context.Context, id uuid.UUID) (*Account, error) {
// if the account is not found
return nil, accountNotFoundErr{id}
}
I like the first one because it's simple, but I don't often see Go code which returns a nil result if the error is non-nil. I think the expectation is that, if the error is nil, the result is valid. The second approach fixes that, but it's also a bit more complicated for callers.
What is an idiomatic approach for handling cases like this in Go?
I have read a lot of posts about custom errors in go. Most of them created their own struct that implements the error interface.
The issue I found with that approach was that I did not manage to easily check if an error was of a certain type. The same way, you may be able to check some std lib error like if error == EOF.
Therefore, my favourite way to do that is creating a simple var with erros.New.
var ErrNotFound = errors.New("Resource was not found")
func main() {
err := raise()
if err == ErrNotFound {
fmt.Println("impossibru")
return
}
if err != nil {
fmt.Println("unexpected error")
return
}
}
func raise() error {
return ErrNotFound
}
https://play.golang.com/p/s0ZQfsdLqxB
As #Gavin pointed out in the comments, if you want to provide more context to the error by wrapping it with fmt.Errorf, you need to use errors.Is to check if the specific error was wrapped.
var ErrNotFound = errors.New("Resource was not found")
func main() {
err := raise(42)
if errors.Is(err, ErrNotFound) {
fmt.Println(err)
return
}
if err != nil {
fmt.Println("unexpected error")
return
}
}
func raise(id int) error {
return fmt.Errorf("id %d does not exist, error: %w", id, ErrNotFound)
}
https://play.golang.com/p/hSrkb1Xp4Hn
Am new to go programming language, and I have a requirement to create Redis PubSub with websocket.
My reference code is here
https://github.com/vortec/orchestrate
Am using following libraries
"golang.org/x/net/websocket"
"github.com/garyburd/redigo/redis"
Everything is working for me like this way, but I don't understand what is "websocket.Handler(handleWSConnection)" here.
I need 2 different go routes for /ws-subscribe and /ws-publish. I don't know anything wrong in this concept?
Doubts
Can I do this way, http.HandleFunc("/ws", handleWSConnection) // Tried this way but am getting "not enough arguments in call to handleWSConnection"
Is there any way to call "handleWSConnection()" as a normal function.
Any suggestions how to write /ws-publish as a different go route
Following is my code
main function
func (wsc *WSConnection) ReadWebSocket() {
for {
var json_data []byte
var message WSMessage
// Receive data from WebSocket
err := websocket.Message.Receive(wsc.socket, &json_data)
if err != nil {
return
}
// Parse JSON data
err = json.Unmarshal(json_data, &message)
if err != nil {
return
}
switch message.Action {
case "SUBSCRIBE":
wsc.subscribe.Subscribe(message.Channel)
case "UNSUBSCRIBE":
wsc.subscribe.Unsubscribe(message.Channel)
case "PUBLISH":
wsc.publish.Conn.Do("PUBLISH", message.Channel, message.Data)
}
}
}
func handleWSConnection(socket *websocket.Conn) {
wsc := &WSConnection {socket: socket}
defer wsc.Uninitialize()
wsc.Initialize()
go wsc.ProxyRedisSubscribe()
wsc.ReadWebSocket()
}
func serveWeb() {
http.Handle("/ws", websocket.Handler(handleWSConnection)) // I need to call this route as funciton
if err := http.ListenAndServe(":9000", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
Done following way, I dont know is it the proper way to do this
http.HandleFunc("/publish", publishHandler)
func publishHandler(conn http.ResponseWriter, req *http.Request) {
log.Println("PUBLISH HANDLER")
wsHandler := websocket.Handler(func(ws *websocket.Conn) {
handleWSConnection(ws)
})
wsHandler.ServeHTTP(conn, req)
}
I have struct that have a method that serve the response.
type ctrl struct {
*base.AjaxCtrl
file ini.File
}
func (rcv *ctrl) getVar() string {
return mux.Vars(rcv.Req)["location"]
}
func (rcv *ctrl) getFile() string {
return location.JoinPaths(folder, rcv.getVar()+ext)
}
func (rcv *ctrl) upload() {
file, err := ini.LoadFile(rcv.getFile())
if err != nil {
rcv.AddErr("TextError", err.Error())
return
}
rcv.file = file
}
// Convert text to json structure
func (rcv *ctrl) convertToJson() string {
js, err := json.Marshal(rcv.file.Section("text/signup"))
if err != nil {
rcv.AddErr("ConvertError", err.Error())
return ""
}
return string(js)
}
func (rcv *ctrl) serveHttp() (types.SuccsJSON, types.ErrorsJSON) {
rcv.upload()
if rcv.AnyErrors() {
return nil, rcv.Errs
}
str := rcv.convertToJson()
if rcv.AnyErrors() {
return nil, rcv.Errs
}
return c.Sucss, nil
}
The method serveHttp() handle the response to client. As you can see, I am handling the error with the method AnyError() everytime. I find this way is pretty boring and maybe wrong design.
Would it be better to throw a panic instead of error handling?
func (rcv *ctrl) upload() {
file, err := ini.LoadFile(rcv.getFile())
if err != nil {
rcv.AddErr("TextError", err.Error())
panic()
}
rcv.file = file
}
No, it would not be better. Checking your errors is not bad design in Go. Panicking when there is no reason to do so, on the other hand, is bad design, because it's harder to test and debug.
The Go Wiki explicitly discourages the use of panic outside of truly exceptional cases, when the error is unrecoverable and state of the program can't be fixed.