@@ -8,12 +8,16 @@ import (
8
8
"net/url"
9
9
"os"
10
10
"path/filepath"
11
- "strconv"
12
11
"strings"
13
12
14
13
"golang.org/x/net/context"
15
14
)
16
15
16
+ const (
17
+ // ErrorMediaIdentifier is the media type identifier used for error responses.
18
+ ErrorMediaIdentifier = "application/vnd.api.error+json"
19
+ )
20
+
17
21
type (
18
22
// Service is the data structure supporting goa services.
19
23
// It provides methods for configuring a service and running it.
@@ -46,21 +50,18 @@ type (
46
50
Context context.Context
47
51
// Middleware chain
48
52
Middleware []Middleware
49
- // Service-wide error handler
50
- ErrorHandler ErrorHandler
51
53
52
- cancel context.CancelFunc
54
+ cancel context.CancelFunc // Service context cancel signal trigger
53
55
decoderPools map [string ]* decoderPool // Registered decoders for the service
54
56
encoderPools map [string ]* encoderPool // Registered encoders for the service
55
57
encodableContentTypes []string // List of contentTypes for response negotiation
56
58
}
57
59
58
60
// Controller provides the common state and behavior for generated controllers.
59
61
Controller struct {
60
- Name string // Controller resource name
61
- Context context.Context // Controller root context
62
- ErrorHandler ErrorHandler // Controller specific error handler if any
63
- Middleware []Middleware // Controller specific middleware if any
62
+ Name string // Controller resource name
63
+ Context context.Context // Controller root context
64
+ Middleware []Middleware // Controller specific middleware if any
64
65
}
65
66
66
67
// Handler defines the controller handler signatures.
@@ -85,10 +86,9 @@ func New(name string) *Service {
85
86
ctx := UseLogger (context .Background (), NewStdLogger (stdlog ))
86
87
ctx , cancel := context .WithCancel (ctx )
87
88
return & Service {
88
- Name : name ,
89
- ErrorHandler : DefaultErrorHandler ,
90
- Context : ctx ,
91
- Mux : NewMux (),
89
+ Name : name ,
90
+ Context : ctx ,
91
+ Mux : NewMux (),
92
92
93
93
cancel : cancel ,
94
94
decoderPools : map [string ]* decoderPool {},
@@ -143,10 +143,9 @@ func (service *Service) ListenAndServeTLS(addr, certFile, keyFile string) error
143
143
func (service * Service ) NewController (resName string ) * Controller {
144
144
ctx := context .WithValue (service .Context , serviceKey , service )
145
145
return & Controller {
146
- Name : resName ,
147
- Middleware : service .Middleware ,
148
- ErrorHandler : service .ErrorHandler ,
149
- Context : context .WithValue (ctx , "ctrl" , resName ),
146
+ Name : resName ,
147
+ Middleware : service .Middleware ,
148
+ Context : context .WithValue (ctx , "ctrl" , resName ),
150
149
}
151
150
}
152
151
@@ -205,23 +204,6 @@ func (ctrl *Controller) Use(m Middleware) {
205
204
ctrl .Middleware = append (ctrl .Middleware , m )
206
205
}
207
206
208
- // HandleError invokes the controller error handler or - if there isn't one - the service error
209
- // handler.
210
- func (ctrl * Controller ) HandleError (ctx context.Context , rw http.ResponseWriter , req * http.Request , err error ) {
211
- status := 500
212
- if e , ok := err .(* Error ); ok {
213
- status = e .Status
214
- }
215
- go IncrCounter ([]string {"goa" , "handler" , "error" , strconv .Itoa (status )}, 1.0 )
216
- if ctrl .ErrorHandler != nil {
217
- ctrl .ErrorHandler (ctx , rw , req , err )
218
- return
219
- }
220
- if h := ContextService (ctx ).ErrorHandler ; h != nil {
221
- h (ctx , rw , req , err )
222
- }
223
- }
224
-
225
207
// MuxHandler wraps a request handler into a MuxHandler. The MuxHandler initializes the
226
208
// request context by loading the request state, invokes the handler and in case of error invokes
227
209
// the controller (if there is one) or Service error handler.
@@ -231,10 +213,7 @@ func (ctrl *Controller) MuxHandler(name string, hdlr Handler, unm Unmarshaler) M
231
213
// Setup middleware outside of closure
232
214
middleware := func (ctx context.Context , rw http.ResponseWriter , req * http.Request ) error {
233
215
if ! ContextResponse (ctx ).Written () {
234
- if err := hdlr (ctx , rw , req ); err != nil {
235
- ContextService (ctx ).LogInfo ("ERROR" , "err" , err )
236
- ctrl .HandleError (ctx , rw , req , err )
237
- }
216
+ return hdlr (ctx , rw , req )
238
217
}
239
218
return nil
240
219
}
@@ -258,55 +237,27 @@ func (ctrl *Controller) MuxHandler(name string, hdlr Handler, unm Unmarshaler) M
258
237
handler := middleware
259
238
if err != nil {
260
239
handler = func (ctx context.Context , rw http.ResponseWriter , req * http.Request ) error {
261
- ctrl . ErrorHandler ( ctx , rw , req , ErrInvalidEncoding ( err ) )
262
- return nil
240
+ rw . Header (). Set ( "Content-Type" , ErrorMediaIdentifier )
241
+ return ContextResponse ( ctx ). Send ( ctx , 400 , ErrInvalidEncoding ( err ))
263
242
}
264
243
for i := range chain {
265
244
handler = chain [ml - i - 1 ](handler )
266
245
}
267
246
}
268
247
269
248
// Invoke middleware chain, wrap writer to capture response status and length
270
- handler (ctx , ContextResponse (ctx ), req )
271
- }
272
- }
273
-
274
- // DefaultErrorHandler returns a response with status 500 or the status specified in the error if
275
- // an instance of HTTPStatusError.
276
- // It writes the error message to the response body in both cases.
277
- func DefaultErrorHandler (ctx context.Context , rw http.ResponseWriter , req * http.Request , e error ) {
278
- status := 500
279
- var respBody interface {}
280
- switch err := e .(type ) {
281
- case * Error :
282
- status = err .Status
283
- respBody = err
284
- default :
285
- respBody = e .Error ()
286
- }
287
- if status == 500 {
288
- LogError (ctx , e .Error ())
249
+ if err := handler (ctx , ContextResponse (ctx ), req ); err != nil {
250
+ LastResortErrorHandler (ctx , rw , req , err )
251
+ }
289
252
}
290
- ContextResponse (ctx ).Send (ctx , status , respBody )
291
253
}
292
254
293
- // TerseErrorHandler behaves like DefaultErrorHandler except that it does not write to the response
294
- // body for internal errors.
295
- func TerseErrorHandler (ctx context.Context , rw http.ResponseWriter , req * http.Request , e error ) {
296
- status := 500
297
- var respBody interface {}
298
- switch err := e .(type ) {
299
- case * Error :
300
- status = err .Status
301
- if status != 500 {
302
- respBody = err
303
- }
304
- }
305
- if respBody == nil {
306
- respBody = "internal error"
307
- }
308
- if status == 500 {
309
- LogError (ctx , e .Error ())
310
- }
311
- ContextResponse (ctx ).Send (ctx , status , respBody )
255
+ // LastResortErrorHandler is the last thing that can handle an error propagating up the middleware
256
+ // chain and turns all errors into a response indicating an internal error.
257
+ // Ideally all errors are handled at a lower level in the middleware chain so they
258
+ // can be logged properly.
259
+ func LastResortErrorHandler (ctx context.Context , rw http.ResponseWriter , req * http.Request , e error ) {
260
+ LogError (ctx , "Last resort error handler" , "err" , e )
261
+ respBody := fmt .Sprintf ("Internal error: %s" , e ) // Sprintf catches panics
262
+ ContextResponse (ctx ).Send (ctx , 500 , respBody )
312
263
}
0 commit comments