github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/context.go (about) 1 package goa 2 3 import ( 4 "net/http" 5 "net/url" 6 "strconv" 7 8 "golang.org/x/net/context" 9 ) 10 11 // Keys used to store data in context. 12 const ( 13 reqKey key = iota + 1 14 respKey 15 ctrlKey 16 actionKey 17 paramsKey 18 logKey 19 logContextKey 20 errKey 21 securityScopesKey 22 ) 23 24 type ( 25 // RequestData provides access to the underlying HTTP request. 26 RequestData struct { 27 *http.Request 28 29 // Payload returns the decoded request body. 30 Payload interface{} 31 // Params contains the raw values for the parameters defined in the design including 32 // path parameters, query string parameters and header parameters. 33 Params url.Values 34 } 35 36 // ResponseData provides access to the underlying HTTP response. 37 ResponseData struct { 38 http.ResponseWriter 39 40 // The service used to encode the response. 41 Service *Service 42 // ErrorCode is the code of the error returned by the action if any. 43 ErrorCode string 44 // Status is the response HTTP status code. 45 Status int 46 // Length is the response body length. 47 Length int 48 } 49 50 // key is the type used to store internal values in the context. 51 // Context provides typed accessor methods to these values. 52 key int 53 ) 54 55 // NewContext builds a new goa request context. 56 // If ctx is nil then context.Background() is used. 57 func NewContext(ctx context.Context, rw http.ResponseWriter, req *http.Request, params url.Values) context.Context { 58 if ctx == nil { 59 ctx = context.Background() 60 } 61 request := &RequestData{Request: req, Params: params} 62 response := &ResponseData{ResponseWriter: rw} 63 ctx = context.WithValue(ctx, respKey, response) 64 ctx = context.WithValue(ctx, reqKey, request) 65 66 return ctx 67 } 68 69 // WithAction creates a context with the given action name. 70 func WithAction(ctx context.Context, action string) context.Context { 71 return context.WithValue(ctx, actionKey, action) 72 } 73 74 // WithLogger sets the request context logger and returns the resulting new context. 75 func WithLogger(ctx context.Context, logger LogAdapter) context.Context { 76 return context.WithValue(ctx, logKey, logger) 77 } 78 79 // WithLogContext instantiates a new logger by appending the given key/value pairs to the context 80 // logger and setting the resulting logger in the context. 81 func WithLogContext(ctx context.Context, keyvals ...interface{}) context.Context { 82 logger := ContextLogger(ctx) 83 if logger == nil { 84 return ctx 85 } 86 nl := logger.New(keyvals...) 87 return WithLogger(ctx, nl) 88 } 89 90 // WithError creates a context with the given error. 91 func WithError(ctx context.Context, err error) context.Context { 92 return context.WithValue(ctx, errKey, err) 93 } 94 95 // ContextController extracts the controller name from the given context. 96 func ContextController(ctx context.Context) string { 97 if c := ctx.Value(ctrlKey); c != nil { 98 return c.(string) 99 } 100 return "<unknown>" 101 } 102 103 // ContextAction extracts the action name from the given context. 104 func ContextAction(ctx context.Context) string { 105 if a := ctx.Value(actionKey); a != nil { 106 return a.(string) 107 } 108 return "<unknown>" 109 } 110 111 // ContextRequest extracts the request data from the given context. 112 func ContextRequest(ctx context.Context) *RequestData { 113 if r := ctx.Value(reqKey); r != nil { 114 return r.(*RequestData) 115 } 116 return nil 117 } 118 119 // ContextResponse extracts the response data from the given context. 120 func ContextResponse(ctx context.Context) *ResponseData { 121 if r := ctx.Value(respKey); r != nil { 122 return r.(*ResponseData) 123 } 124 return nil 125 } 126 127 // ContextLogger extracts the logger from the given context. 128 func ContextLogger(ctx context.Context) LogAdapter { 129 if v := ctx.Value(logKey); v != nil { 130 return v.(LogAdapter) 131 } 132 return nil 133 } 134 135 // ContextError extracts the error from the given context. 136 func ContextError(ctx context.Context) error { 137 if err := ctx.Value(errKey); err != nil { 138 return err.(error) 139 } 140 return nil 141 } 142 143 // SwitchWriter overrides the underlying response writer. It returns the response 144 // writer that was previously set. 145 func (r *ResponseData) SwitchWriter(rw http.ResponseWriter) http.ResponseWriter { 146 rwo := r.ResponseWriter 147 r.ResponseWriter = rw 148 return rwo 149 } 150 151 // Written returns true if the response was written, false otherwise. 152 func (r *ResponseData) Written() bool { 153 return r.Status != 0 154 } 155 156 // WriteHeader records the response status code and calls the underlying writer. 157 func (r *ResponseData) WriteHeader(status int) { 158 go IncrCounter([]string{"goa", "response", strconv.Itoa(status)}, 1.0) 159 r.Status = status 160 r.ResponseWriter.WriteHeader(status) 161 } 162 163 // Write records the amount of data written and calls the underlying writer. 164 func (r *ResponseData) Write(b []byte) (int, error) { 165 r.Length += len(b) 166 return r.ResponseWriter.Write(b) 167 }