In the example below I've embedded http.ResponseWriter into my own struct called Response. I've also added an extra field called Status. Why can't I access that field from inside my root handler function?
When I print out the type of w in my root handler function it says it's of type main.Response which seems correct and when I print out the values of the struct I can see that Status is there. Why can't I access by going w.Status?
This is the contents of stdout:
main.Response
{ResponseWriter:0xc2080440a0 Status:0}
Code:
package main
import (
"fmt"
"reflect"
"net/http"
)
type Response struct {
http.ResponseWriter
Status int
}
func (r Response) WriteHeader(n int) {
r.Status = n
r.ResponseWriter.WriteHeader(n)
}
func middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := Response{ResponseWriter: w}
h.ServeHTTP(resp, r)
})
}
func root(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("root"))
fmt.Println(reflect.TypeOf(w))
fmt.Printf("%+v\n", w)
fmt.Println(w.Status) // <--- This causes an error.
}
func main() {
http.Handle("/", middleware(http.HandlerFunc(root)))
http.ListenAndServe(":8000", nil)
}
w is a variable of type http.ResponseWriter. ResponseWriter does not have a field or method Status, only your Response type.
http.ResponseWriter is an interface type, and since your Response type implements it (because it embeds ResponseWriter), the w variable may hold a value of dynamic type Response (and in your case it does).
But to access the Response.Status field, you have to convert it to a value of type Response. For that use Type assertion:
if resp, ok := w.(Response); ok {
// resp is of type Response, you can access its Status field
fmt.Println(resp.Status) // <--- properly prints status
}
Related
i wanted to create custom errors for my authentication service that i am currently working on.
Since i have to create a error for every http responses, and i am fairly new to golang i am facing difficulties.
The below code is the replica of the javascript code code that i wanted to implement here in go.
export abstract class CustomError extends Error {
abstract statusCode: number;
constructor(message: string) {
super(message);
Object.setPrototypeOf(this, CustomError.prototype);
}
abstract serializeErrors(): { message: string; field?: string }[];
}
To create extended classes based on the custom error like this
import { CustomError } from "./custom-error";
export class NotFoundError extends CustomError {
statusCode = 404;
constructor() {
super("Route not found");
Object.setPrototypeOf(this, NotFoundError.prototype);
}
serializeErrors() {
return [{ message: "Not Found" }];
}
}
so that i can be thrown or logged from the main file i.e.
const existingUser = await User.findOne({ email });
if (existingUser) {
throw new BadRequestError("Email is already in use");
}
so in simple language i wanted to create an object/model of CustomErrors that can help to create more diverse Erros like BadRequestError()
so, i need help regarding creating this one. And this is my first question here
In Go you create a custom error type simply by implementing the error interface.
The error interface is:
type error interface {
Error() string
}
[if you're completely new to Go, I suggest starting with the Tour of Go on interfaces]
For example:
type SyntaxError struct {
msg string // description of error
Offset int64 // error occurred after reading Offset bytes
}
func (e *SyntaxError) Error() string { return e.msg }
See this official Go blog post for more details.
This is for error types; if you're specifically looking for HTTP errors, those in a server are done by writing an error status to a http.ResponseWriter along with the error message you need, and you can use the helper http.Error for this. Example:
func myHandler(w http.ResponseWriter, req *http.Request) {
if (somethingIsWrong) {
http.Error(w, "the error message", http.StatusBadRequest)
}
}
The last param to http.Error is the HTTP status; check out the net/http stdlib package docs for the options you have there.
Now, to connect the two, http.Error would typically use the Error() method of your custom error as the message, and the status is really application specific.
If you want http error responses in plain text, http.Error should be your choice. But if your authentication service needs error response in specific format (JSON/XML) then you need to create custom http errors which can be serialized and written into the response.
To create custom http error responses for JSON format (for XML format modify serialization), first you need to create some types -
type ErrFields map[string]string // Error field-value pair type
type ResponseError struct {
Msg string `json:"message"` // Error message
Status int `json:"status"` // Http status code
Data ErrFields // For extra error fields e.g. reason, details, etc.
}
type ErrList []ResponseError // Multiple http errors type
Methods for ResponseError type -
// AddErrField adds a new field to the response error with given key and value
func (err *ResponseError) AddErrField(key, value string) {
if err.Data == nil {
err.Data = make(ErrFields)
}
err.Data[key] = value
}
// RemoveErrField removes existing field matching given key from response error
func (err *ResponseError) RemoveErrField(key string) {
delete(err.Data, key)
}
// MarshalJSON marshals the response error into json
func (err *ResponseError) MarshalJSON() ([]byte, error) {
// Determine json field name for error message
errType := reflect.TypeOf(*err)
msgField, ok := errType.FieldByName("Msg")
msgJsonName := "message"
if ok {
msgJsonTag := msgField.Tag.Get("json")
if msgJsonTag != "" {
msgJsonName = msgJsonTag
}
}
// Determine json field name for error status code
statusField, ok := errType.FieldByName("Status")
statusJsonName := "status"
if ok {
statusJsonTag := statusField.Tag.Get("json")
if statusJsonTag != "" {
statusJsonName = statusJsonTag
}
}
fieldMap := make(map[string]string)
fieldMap[msgJsonName] = err.Msg
fieldMap[statusJsonName] = fmt.Sprintf("%d", err.Status)
for key, value := range err.Data {
fieldMap[key] = value
}
return json.Marshal(fieldMap)
}
// SerializeJSON converts response error into serialized json string
func (resErr *ResponseError) SerializeJSON() (string, error) {
value, err := json.Marshal(resErr)
if err != nil {
return "", err
}
return string(value), nil
}
Methods for ErrList type -
// SerializeJSON converts error list into serialized json string
func (errList ErrList) SerializeJSON() (string, error) {
value, err := json.Marshal(errList)
if err != nil {
return "", err
}
return string(value), nil
}
Now you can create custom http error responses by creating different values of ResponseError type -
// Error returns a general response error
func Error(msg string, status int) ResponseError {
return ResponseError{msg, status, nil}
}
// Errors returns a error list containing given response errors
func Errors(errors ...ResponseError) ErrList {
return errors
}
// Specific HTTP error responses
func ErrorNotFound() ResponseError {
return Error("not found", http.StatusNotFound)
}
func ErrorBadRequest() ResponseError {
return Error("bad request", http.StatusBadRequest)
}
func ErrorInternalServerError() ResponseError {
return Error("internal server error", http.StatusInternalServerError)
}
func ErrorForbidden() ResponseError {
return Error("forbidden", http.StatusForbidden)
}
You can add/remove custom fields to the ResponseError values -
notFoundErr := ErrorNotFound()
notFoundErr.AddErrField("reason", "given 'id' does not exist")
notFoundErr.RemoveErrField("reason")
Since in Go there is no concept of throw, you can only return response error from a function -
func Foo() (resErr ResponseError, ok bool) {
...
if existingUser {
resErr = ErrorBadRequest()
resErr.AddErrField("reason", "Email is already in use")
return resErr, true
}
...
return ResponseError{}, false
}
To serialize response error into JSON -
resErr, ok := Foo()
if !ok {
json, err := resErr.SerializeJSON()
if err != nil {
// Handle serialization error
}
}
See the Go playground example here.
Take this sample of code (playground):
package main
import (
"fmt"
)
type Foo struct {
Name string
}
var data = make(map[string]interface{})
func main() {
data["foo"] = &Foo{"John"}
foo := data["foo"].(*Foo)
fmt.Println(foo.Name)
}
When I add something to data, the type turns into an interface{}, so when I later retrieve that value I have to assert the original type back onto it. Is there a way to, for example, define a getter function for data which will automagically assert the type?
Not really, unless you turn to reflect and try to get the type of the interface that way.
But the idiomatic (and faster) way remains the type assertion (a "type conversion" which must be checked at runtime, since data only contains interface{} values).
If data were to reference a specific interface (instead of the generic interface{} one), like I mentioned here, then you could use a Name() method defined directly on it.
You can do something like this, but you might want to think about your design.. It is very rare that you need to do this kind of things.
http://play.golang.org/p/qPSxRoozaM
package main
import (
"fmt"
)
type GenericMap map[string]interface{}
func (gm GenericMap) GetString(key string) string {
return gm[key].(string)
}
func (gm GenericMap) GetFoo(key string) *Foo {
return gm[key].(*Foo)
}
func (gm GenericMap) GetInt(key string) int {
return gm[key].(int)
}
var data = make(GenericMap)
type Foo struct {
Name string
}
func main() {
data["foo"] = &Foo{"John"}
foo := data.GetFoo("foo")
fmt.Println(foo.Name)
}
You might want to add error checking, in case the key does not exists or is not the expected type.
I am trying to write a method on a struct that takes in a interface type and returns that interface type but converted to the appropriate type.
type Model interface {
GetEntity()
}
type TrueFalseQuestions struct {
//some stuff
}
func (q *TrueFalseQuestions) GetEntity() {
//some stuff
}
type MultiQuestions struct {
//some stuff
}
func (q *MultiQuestions) GetEntity() {
//some stuff
}
type Manager struct {
}
func (man *Manager) GetModel(mod Model) Model {
mod.GetEntity()
return mod
}
func main() {
var man Manager
q := TrueFalseQuestions {}
q = man.GetModel(&TrueFalseQuestions {})
}
So when I call GetModel() with type TrueFalseQuestions I want to automatically return a TrueFalseQuestions type. I figured that would mean that my GetModel() method should return a Model type. That way if I pass a MultiQuestion type a MultiQuestion struct is returned.
You can't directly return a TrueFalseQuestions when the return type is Model. It will always be implicitly wrapped in a Model interface.
To get the TrueFalseQuestions back, you need to use a type-assertion. (you also need watch out for pointers vs values)
// this should be a pointer, because the interface methods all have pointer receivers
q := &TrueFalseQuestions{}
q = man.GetModel(&TrueFalseQuestions{}).(*TrueFalseQuestions)
That of course can panic if you got a MultiQuestions, so you should check the ok value, or use a type switch
switch q := man.GetModel(&TrueFalseQuestions{}).(type) {
case *TrueFalseQuestions:
// q isTrueFalseQuestions
case *MultiQuestions:
// q is MultiQuestions
default:
// unexpected type
}
You can't, however you can use type assertion on the returned value.
func main() {
var man Manager
tfq := &TrueFalseQuestions{}
q := man.GetModel(tfq)
if v, ok := q.(*TrueFalseQuestions); ok {
fmt.Println("v is true/false", v)
} else if v, ok := q.(*MultiQuestions); ok {
fmt.Println("v is mq", v)
} else {
fmt.Println("unknown", q)
}
}
playground
I have the following struct which contains a net/http.Request:
type MyRequest struct {
http.Request
PathParams map[string]string
}
Now I want to initialize the anonymous inner struct http.Request in the following function:
func New(origRequest *http.Request, pathParams map[string]string) *MyRequest {
req := new(MyRequest)
req.PathParams = pathParams
return req
}
How can I initialize the inner struct with the parameter origRequest?
req := new(MyRequest)
req.PathParams = pathParams
req.Request = origRequest
or...
req := &MyRequest{
PathParams: pathParams
Request: origRequest
}
See: http://golang.org/ref/spec#Struct_types for more about embedding and how the fields get named.
What about:
func New(origRequest *http.Request, pathParams map[string]string) *MyRequest {
return &MyRequest{*origRequest, pathParams}
}
It shows that instead of
New(foo, bar)
you might prefer just
&MyRequest{*foo, bar}
directly.
As Jeremy shows above, the "name" of an anonymous field is the same as the type of the field. So if the value of x were a struct containing an anonymous int, then x.int would refer to that field.
I am trying to use the svgo package to plot points on an svg file and display that using the web browser. From looking at the net/http documetation, I don't know how I could pass arguments into my svgWeb function.
The example below compiles and displays a triangle and a line in my web-browser, but what I would really like to do is plot xpts and ypts using the Polyline method. How can I pass the appropriate arguments or restructure this example to accomplish that task?
package main
import (
"github.com/ajstarks/svgo"
"log"
"net/http"
)
func svgWeb(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
xpts := []int{1, 200, 5}
ypts := []int{200, 400, 300}
s := svg.New(w)
s.Start(500, 500)
s.Line(5, 10, 400, 400, "stroke:black")
s.Polyline(xpts, ypts, "stroke:black")
s.End()
}
//// Main Program function
//////////////////////////////
func main() {
xpts := []int{}
ypts := []int{}
for i := 0; i < 100; i++ {
xpts = append(xpts, i)
xpts = append(ypts, i+5)
}
http.Handle("/economy", http.HandlerFunc(svgWeb))
err := http.ListenAndServe(":2003", nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
If your arguments are meant to be supplied by the client, then they should be passed to your handler via the http.Request.
But if what you are trying to do is to drive your svgWeb handler by points that are not supplied by the client request, but rather by some other functions in your application generating these values internally, then one way would be to structure your handler into a struct and use member attributes.
The struct may look like this:
type SvgManager struct {
Xpts, Ypts []int
}
func (m *SvgManager) SvgWeb(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
s := svg.New(w)
s.Start(500, 500)
s.Line(5, 10, 400, 400, "stroke:black")
s.Polyline(m.Xpts, m.Ypts, "stroke:black")
s.End()
}
Then in your main:
manager := new(SvgManager)
for i := 0; i < 100; i++ {
manager.Xpts = append(manager.Xpts, i)
manager.Ypts = append(manager.Ypts, i+5)
}
// I only did this assignment to make the SO display shorter in width.
// Could have put it directly in the http.Handle()
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
manager.SvgWeb(w, req)
})
http.Handle("/economy", handler)
Now you have an SvgManager instance that could contain other handlers as well, and can be updated to affect the output of their handlers.
Satisfying the Handler interface
As mentioned by #Atom in the comments, you could completely avoid the closure and the wrapper by simply renaming your method to ServeHTTP. This would satisfy the Handler interface
func (m *SvgManager) ServeHTTP(w http.ResponseWriter, req *http.Request) {
...
manager := new(SvgManager)
http.Handle("/economy", manager)
You should define your function inside main as an anonymous function. This way, it can refer to the local variables xpts and ypts (the function will be a closure).