Idiomatic way to reference custom error codes

192 views
Skip to first unread message

JohnGB

unread,
Jan 25, 2017, 12:16:33 PM1/25/17
to golang-nuts
I have an HTTP API written in Go, and to give more fine grained responses to the client app, we return our own error codes along with the standard HTTP status codes  Along a similar line to what Twitter does with its error codes.  The correct HTTP status code can always be determined from our internal error code, so any HTTP handler that wants to return an error response, only has to call our error response function, and pass in our custom error code, and from that we can determine the error message and http response that we should give in the HTTP response.  What I am not sure about, is the best way to structure this.

One option is to define our error codes as constants with iota.  Something like:

const (
  OK                              int = iota + 1                                                               
  ErrBadRequest                                               
  ErrRecipientNotExist
  ...
)

And then have some sort of switch lookup to return the HTTP response and the error message to return.  Something like:

func inspectStatusCode(errCode int) (httpStatus int, errorMsg string) {
  switch errCode {
  case OK:
    return http.StatusOK, "OK"
  case ErrBadRequest:
    return http.StatusBadRequest, "Poorly formatted request"
  case ErrRecipientNotExist:
    return http.StatusNotFound, "The entity does not exist"
  ...
  }
  ...
}

This method allows me to not have to directly specify an int value for each response.

Alternatively I could define a struct that contains all of the data directly, but with an explicit response code.  Something like:

type response struct {
  code int
  message string
  httpStatus int
}

And then have a large number of global variables to reference the response.  Something like:

var (
  ErrBadRequest = errResp{
    code: 10,
    message: "Poorly formatted request",
    httpStatus: http.StatusBadRequest,
  }
  ...
)

Only here I don't like that I'm using global variables, rather than constants, and I don't like that I have to specify each error code directly (but then I'll have to do that in the documentation anyway).

Is there a better way of handling this than one of these two methods?  Otherwise I'd love to get feedback on the two methods that I've described.

pierre...@gmail.com

unread,
Jan 25, 2017, 1:02:18 PM1/25/17
to golang-nuts
Maybe use a map along the lines of: https://play.golang.org/p/YDF4q6cTyX ?

Manlio Perillo

unread,
Jan 25, 2017, 1:09:37 PM1/25/17
to golang-nuts
Il giorno mercoledì 25 gennaio 2017 18:16:33 UTC+1, JohnGB ha scritto:
I have an HTTP API written in Go, and to give more fine grained responses to the client app, we return our own error codes along with the standard HTTP status codes  Along a similar line to what Twitter does with its error codes.

Since it is an HTTP Api, what about returning a standard HTTP status line, maybe with some non standard status code) and additional details about the error in the response body, JSON encoded?

> [...]

Regards
Manlio

John Beckett

unread,
Jan 26, 2017, 1:37:49 AM1/26/17
to Manlio Perillo, golang-nuts
Since it is an HTTP Api, what about returning a standard HTTP status line, maybe with some non standard status code) and additional details about the error in the response body, JSON encoded?

That is exactly what we do.  We return a HTTP status, but in addition, we return a JSON encoded body with our internal status code, and description.  

The question is more about the cleanest and easiest way to maintain this as the HTTP status is determined by our internal status code (which is far more fine grained than what we can achieve with only a HTTP status).  I'm trying to find the cleanest way to implement a reference to our internal status code so that we can also extract the other information (i.e. HTTP status, and description) from our status code.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/VzDfhN4GtIY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

John Beckett

unread,
Jan 26, 2017, 1:42:55 AM1/26/17
to pierre...@gmail.com, golang-nuts
Thanks Pierre, that looks like a simple and quite clean implementation.  Although it's easier to use an iota for the numbering, does that open up any issues with keeping the documentation up to date?  I know it's easier to program and extend, but it's also less obvious when codes change.

Is there a reason why you've used:

var customHTTPErrorRegistry = map[errCode]customHTTPError{
1: {http.StatusOK, "OK"},
}

instead of say:

var customHTTPErrorRegistry = map[errCode]customHTTPError{
OK: {http.StatusOK, "OK"},
}


Tamás Gulácsi

unread,
Jan 26, 2017, 2:54:00 AM1/26/17
to golang-nuts, manlio....@gmail.com
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.


A map is easier to extend in less coupled places than a switch, but may hide and cause problems if used unsparingly
(use it in init() functions and through an exported Register method, only).

Manlio Perillo

unread,
Jan 26, 2017, 8:04:36 AM1/26/17
to golang-nuts, manlio....@gmail.com
Il giorno giovedì 26 gennaio 2017 07:37:49 UTC+1, JohnGB ha scritto:
Since it is an HTTP Api, what about returning a standard HTTP status line, maybe with some non standard status code) and additional details about the error in the response body, JSON encoded?

That is exactly what we do.  We return a HTTP status, but in addition, we return a JSON encoded body with our internal status code, and description.  


Ok.
I misinterpreted your question, sorry .

The question is more about the cleanest and easiest way to maintain this as the HTTP status is determined by our internal status code (which is far more fine grained than what we can achieve with only a HTTP status).  I'm trying to find the cleanest way to implement a reference to our internal status code so that we can also extract the other information (i.e. HTTP status, and description) from our status code.


One solution I can think of is:

1) Use the custom response type you defined in the original message,
  with the internal code, message and standard HTTP status code
2) Define the internal status code in an asset file, in an easy to write easy to parse format
3) Use go generate to generate the Go variables
4) Use some script to also generate the documentation

As you wrote, you can have constants; but this is not a problem.

> [...]


Manlio 

pierre...@gmail.com

unread,
Jan 27, 2017, 2:46:09 AM1/27/17
to golang-nuts, pierre...@gmail.com


Le jeudi 26 janvier 2017 07:42:55 UTC+1, JohnGB a écrit :
Thanks Pierre, that looks like a simple and quite clean implementation.  Although it's easier to use an iota for the numbering, does that open up any issues with keeping the documentation up to date?  I know it's easier to program and extend, but it's also less obvious when codes change.

It's up to you. If you expect the codes to change a lot, then just list them as constants.
 

Is there a reason why you've used:

var customHTTPErrorRegistry = map[errCode]customHTTPError{
1: {http.StatusOK, "OK"},
}

instead of say:

var customHTTPErrorRegistry = map[errCode]customHTTPError{
OK: {http.StatusOK, "OK"},
}


No. The second option is better I think.
 

To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages