├── .gitignore ├── .travis.yml ├── GenericRequestChainHandler.go ├── LICENSE ├── README.md ├── RequestChainHandler.go ├── middleware.go ├── middleware_test.go └── templates ├── jsonResponseHandler.go ├── logHandler.go └── pongHandler.go /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | debug* 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.7 4 | - 1.8 5 | 6 | script: 7 | - go test -v ./... -------------------------------------------------------------------------------- /GenericRequestChainHandler.go: -------------------------------------------------------------------------------- 1 | package goMiddlewareChain 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/julienschmidt/httprouter" 8 | ) 9 | 10 | // GenericRequestChainHandler is the generic requestChainHandler. 11 | // 12 | // checkResponseOfEveryHandler -> if true: check the responseCode after every handler. If httpStatus is != 0 or 200, stop to move on. 13 | // restrictHandler -> if != nil: pass first this handler. Use this for example for AuthHandler 14 | // responseHandler -> if != nil: pass at last this handler. Use this for example for a JSON-converter 15 | // handlers -> all your handler-chain or middlewares or or or 16 | func GenericRequestChainHandler(checkResponseOfEveryHandler bool, restrictHandler RestrictContextHandler, responseHandler ResponseHandler, handlers ...ContextHandler) httprouter.Handle { 17 | return httprouter.Handle(func(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { 18 | payload := Response{} 19 | rootContext := context.Background() 20 | var runningContext context.Context 21 | 22 | // check restriction 23 | allowed := true 24 | if restrictHandler != nil { 25 | runningContext, allowed = restrictHandler(rootContext, &payload, request, params) 26 | } 27 | 28 | if allowed { 29 | // iterate all handlers 30 | runningContext = rootContext 31 | for _, handler := range handlers { 32 | runningContext = handler(runningContext, &payload, request, params) 33 | if checkResponseOfEveryHandler && (payload.Status.Code != http.StatusOK && payload.Status.Code != 0) { 34 | break 35 | } 36 | } 37 | } else if payload.Status.Code == 0 { 38 | payload.Status.Code = http.StatusUnauthorized 39 | payload.Status.Message = "failed by passing restrictHandler" 40 | } 41 | 42 | // pass responseHandler 43 | if responseHandler != nil { 44 | responseHandler(&payload, writer, request, params) 45 | } 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tobias Eiss, Daniel Becker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goMiddlewareChain [![Build Status](https://travis-ci.org/TobiEiss/goMiddlewareChain.svg?branch=master)](https://travis-ci.org/TobiEiss/goMiddlewareChain) 2 | 3 | This is an express.js-like-middleware-chain for [julienschmidt's httprouter](https://github.com/julienschmidt/httprouter) 4 | 5 | You can write your own middleware, and chain this to a lot of other middlewares (logging, auth,...). 6 | 7 | ## Getting started 8 | 9 | ### Install goMiddlewareChain 10 | `go get github.com/TobiEiss/goMiddlewareChain` 11 | 12 | ### Your first API 13 | 14 | Here a simple example with a simple Ping-Pong-Handler chained with a JSONResponseHandler (from templates). 15 | 16 | ```golang 17 | package main 18 | 19 | import ( 20 | "fmt" 21 | "log" 22 | "net/http" 23 | 24 | "github.com/julienschmidt/httprouter" 25 | "github.com/TobiEiss/goMiddlewareChain" 26 | "github.com/TobiEiss/goMiddlewareChain/templates" 27 | ) 28 | 29 | // Ping return a simply pong 30 | func Ping(response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) { 31 | // simply pong 32 | response.Status.Code = http.StatusOK 33 | response.Data = "pong" 34 | } 35 | 36 | func main() { 37 | router := httprouter.New() 38 | router.GET("/api/v0/ping", goMiddlewareChain.RequestChainHandler(templates.JSONResponseHandler, Ping)) 39 | 40 | log.Fatal(http.ListenAndServe(":8080", router)) 41 | } 42 | ``` 43 | 44 | After running this code, run `curl localhost:8080/api/v0/ping` in a terminal. 45 | You will get the following: 46 | ```json 47 | { 48 | "data":"pong", 49 | "msg":"OK", 50 | "status":200 51 | } 52 | ``` 53 | Isn't it cool? 54 | 55 | ## restricted-requestChainHandler 56 | In some cases you need a restriction to apply requestChain. For example an auth-restriction. 57 | You can use the `RestrictedRequestChainHandler`. If the `RestrictHandler` failed, the code doesn't pass the chain. 58 | 59 | Same example with Auth: 60 | 61 | ```golang 62 | package main 63 | 64 | import ( 65 | "log" 66 | "net/http" 67 | 68 | "github.com/TobiEiss/goMiddlewareChain" 69 | "github.com/TobiEiss/goMiddlewareChain/templates" 70 | "github.com/julienschmidt/httprouter" 71 | ) 72 | 73 | // Ping return a simply pong 74 | func Ping(response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) { 75 | // simply pong 76 | response.Status.Code = http.StatusOK 77 | response.Data = "pong" 78 | } 79 | 80 | func Auth(response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) bool { 81 | user := request.Header.Get("X-User") 82 | return user == "HomerSimpson" 83 | } 84 | 85 | func main() { 86 | router := httprouter.New() 87 | router.GET("/api/v0/ping", goMiddlewareChain.RestrictedRequestChainHandler(Auth, templates.JSONResponseHandler, Ping)) 88 | 89 | log.Fatal(http.ListenAndServe(":8080", router)) 90 | } 91 | ``` 92 | 93 | Now run `curl --header "X-User: HomerSimpson" localhost:8080/api/v0/ping` in your terminal. You will get: 94 | ```json 95 | { 96 | "data":"pong", 97 | "msg":"OK", 98 | "status":200 99 | } 100 | ``` 101 | 102 | If you run `curl --header "X-User: BartSimpson" localhost:8080/api/v0/ping`, you get: 103 | ```json 104 | { 105 | "msg":"failed by passing restrictHandler", 106 | "status":401 107 | } 108 | ``` 109 | 110 | ## handler from templates 111 | - [logHandler](https://github.com/TobiEiss/goMiddlewareChain/blob/master/templates/logHandler.go) is an easy handler to log all accesses 112 | - [jsonResponseHandler](https://github.com/TobiEiss/goMiddlewareChain/blob/master/templates/jsonResponseHandler.go) try to transform your response to valid json 113 | 114 | You need more handler? Just let us now this. 115 | -------------------------------------------------------------------------------- /RequestChainHandler.go: -------------------------------------------------------------------------------- 1 | package goMiddlewareChain 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/julienschmidt/httprouter" 8 | ) 9 | 10 | // RequestChainHandler chains all handler 11 | func RequestChainHandler(responseHandler ResponseHandler, handlers ...Handler) httprouter.Handle { 12 | return RequestChainHandlerWithResponseCheck(false, responseHandler, handlers...) 13 | } 14 | 15 | // RequestChainHandlerWithResponseCheck chains all handler and check every response 16 | func RequestChainHandlerWithResponseCheck(checkResponseOfEveryHandler bool, responseHandler ResponseHandler, handlers ...Handler) httprouter.Handle { 17 | return httprouter.Handle(func(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { 18 | payload := Response{} 19 | 20 | // iterate all handlers 21 | for _, handler := range handlers { 22 | handler(&payload, request, params) 23 | if checkResponseOfEveryHandler && (payload.Status.Code != http.StatusOK && payload.Status.Code != 0) { 24 | break 25 | } 26 | } 27 | 28 | // pass responseHandler 29 | responseHandler(&payload, writer, request, params) 30 | }) 31 | } 32 | 33 | // RequestChainContextHandler chains all handler 34 | func RequestChainContextHandler(responseHandler ResponseHandler, handlers ...ContextHandler) httprouter.Handle { 35 | return httprouter.Handle(func(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { 36 | payload := Response{} 37 | rootContext := context.Background() 38 | 39 | // iterate all handlers 40 | var runningContext context.Context 41 | runningContext = rootContext 42 | for _, handler := range handlers { 43 | runningContext = handler(runningContext, &payload, request, params) 44 | } 45 | 46 | // pass responseHandler 47 | responseHandler(&payload, writer, request, params) 48 | }) 49 | } 50 | 51 | // RestrictedRequestChainHandler need a RestrictHandler. 52 | // A RestrictHandler returns bool if call is allowed. 53 | func RestrictedRequestChainHandler(restrictHandler RestrictHandler, responseHandler ResponseHandler, handlers ...Handler) httprouter.Handle { 54 | return RestrictedRequestChainHandlerWithResponseCheck(false, restrictHandler, responseHandler, handlers...) 55 | } 56 | 57 | // RestrictedRequestChainHandlerWithResponseCheck need a RestrictHandler. 58 | // If checkResponseOfEveryHandler is true, handler check every response. 59 | func RestrictedRequestChainHandlerWithResponseCheck(checkResponseOfEveryHandler bool, restrictHandler RestrictHandler, responseHandler ResponseHandler, handlers ...Handler) httprouter.Handle { 60 | return httprouter.Handle(func(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { 61 | payload := Response{} 62 | 63 | // check restriction 64 | allowed := restrictHandler(&payload, request, params) 65 | 66 | if allowed { 67 | // iterate all handlers 68 | for _, handler := range handlers { 69 | handler(&payload, request, params) 70 | if checkResponseOfEveryHandler && (payload.Status.Code != http.StatusOK && payload.Status.Code != 0) { 71 | break 72 | } 73 | } 74 | } else if payload.Status.Code == 0 { 75 | payload.Status.Code = http.StatusUnauthorized 76 | payload.Status.Message = "failed by passing restrictHandler" 77 | } 78 | 79 | // pass ResponseHandler 80 | responseHandler(&payload, writer, request, params) 81 | }) 82 | } 83 | 84 | // RestrictedRequestChainContextHandler need a RestrictHandler. 85 | // A RestrictHandler returns bool if call is allowed. 86 | func RestrictedRequestChainContextHandler(restrictHandler RestrictContextHandler, responseHandler ResponseHandler, handlers ...ContextHandler) httprouter.Handle { 87 | return RestrictedRequestChainContextHandlerWithResponseCheck(false, restrictHandler, responseHandler, handlers...) 88 | } 89 | 90 | // RestrictedRequestChainContextHandlerWithResponseCheck exec all handlers 91 | // If checkResponseOfEveryHandler is true, handler check every response. 92 | func RestrictedRequestChainContextHandlerWithResponseCheck(checkResponseOfEveryHandler bool, restrictHandler RestrictContextHandler, responseHandler ResponseHandler, handlers ...ContextHandler) httprouter.Handle { 93 | return httprouter.Handle(func(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { 94 | payload := Response{} 95 | rootContext := context.Background() 96 | var runningContext context.Context 97 | 98 | // check restriction 99 | runningContext, allowed := restrictHandler(rootContext, &payload, request, params) 100 | 101 | if allowed { 102 | // iterate all handlers 103 | runningContext = rootContext 104 | for _, handler := range handlers { 105 | runningContext = handler(runningContext, &payload, request, params) 106 | if checkResponseOfEveryHandler && (payload.Status.Code != http.StatusOK && payload.Status.Code != 0) { 107 | break 108 | } 109 | } 110 | } else if payload.Status.Code == 0 { 111 | payload.Status.Code = http.StatusUnauthorized 112 | payload.Status.Message = "failed by passing restrictHandler" 113 | } 114 | 115 | // pass ResponseHandler 116 | responseHandler(&payload, writer, request, params) 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /middleware.go: -------------------------------------------------------------------------------- 1 | package goMiddlewareChain 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/julienschmidt/httprouter" 8 | ) 9 | 10 | // Status represents the handler status 11 | type Status struct { 12 | Code int 13 | Message string 14 | } 15 | 16 | // Response struct for Handlers 17 | type Response struct { 18 | Data interface{} 19 | Status Status 20 | } 21 | 22 | // Handler represent a chainable Handler (middleware-like) 23 | type Handler func(*Response, *http.Request, httprouter.Params) 24 | 25 | // RestrictHandler restricts to handle following handlers 26 | type RestrictHandler func(*Response, *http.Request, httprouter.Params) bool 27 | 28 | // ResponseHandler required for every Endpoint 29 | type ResponseHandler func(*Response, http.ResponseWriter, *http.Request, httprouter.Params) 30 | 31 | // ContextHandler a handler with the go-lang-context 32 | type ContextHandler func(context.Context, *Response, *http.Request, httprouter.Params) context.Context 33 | 34 | // RestrictContextHandler restrict handler for contextHandler 35 | type RestrictContextHandler func(context.Context, *Response, *http.Request, httprouter.Params) (context.Context, bool) 36 | 37 | // ContextKey to map ContextValues 38 | type ContextKey struct { 39 | Key string 40 | } 41 | -------------------------------------------------------------------------------- /middleware_test.go: -------------------------------------------------------------------------------- 1 | package goMiddlewareChain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "testing" 8 | 9 | "net/http/httptest" 10 | 11 | "encoding/json" 12 | 13 | "reflect" 14 | 15 | "github.com/julienschmidt/httprouter" 16 | ) 17 | 18 | // response handler which write response to writer 19 | var responseHandler = func(response *Response, writer http.ResponseWriter, _ *http.Request, _ httprouter.Params) { 20 | bodyMap := map[string]interface{}{"data": response.Data, "status": response.Status.Code} 21 | bodyJSON, _ := json.Marshal(bodyMap) 22 | fmt.Fprintf(writer, "%s", bodyJSON) 23 | } 24 | 25 | func TestMain(t *testing.T) { 26 | 27 | var tests = []struct { 28 | handler Handler 29 | restrictHandler RestrictHandler 30 | expectedData interface{} 31 | }{ 32 | // Test 0: 33 | // Check if chain works 34 | { 35 | handler: func(response *Response, _ *http.Request, _ httprouter.Params) { 36 | response.Data = "data" 37 | }, 38 | expectedData: "data", 39 | }, 40 | // Test 1: 41 | // Check if restrictedChain works with restriction true 42 | { 43 | handler: func(response *Response, _ *http.Request, _ httprouter.Params) { 44 | response.Data = "data" 45 | }, 46 | restrictHandler: func(response *Response, request *http.Request, _ httprouter.Params) bool { 47 | return true 48 | }, 49 | expectedData: "data", 50 | }, 51 | } 52 | 53 | // run all tests 54 | for index, test := range tests { 55 | // recorder and request to simulate test 56 | recorder := httptest.NewRecorder() 57 | request, err := http.NewRequest("POST", "", nil) 58 | 59 | // build chain 60 | if reflect.ValueOf(test.restrictHandler).IsNil() { 61 | handlerChain := RequestChainHandler(responseHandler, test.handler) 62 | handlerChain(recorder, request, nil) 63 | } else { 64 | restrictedHandleChain := RestrictedRequestChainHandler(test.restrictHandler, responseHandler, test.handler) 65 | restrictedHandleChain(recorder, request, nil) 66 | } 67 | 68 | // check result 69 | var responseBody map[string]interface{} 70 | json.Unmarshal([]byte(recorder.Body.String()), &responseBody) 71 | if err != nil || responseBody["data"] != test.expectedData { 72 | t.Errorf("Test %v failed: expected data is not equals response.data; expected: %v; response: %v", index, test.expectedData, responseBody["data"]) 73 | } 74 | } 75 | } 76 | 77 | func TestContextHandler(t *testing.T) { 78 | 79 | var key = struct { 80 | key string 81 | }{ 82 | key: "key", 83 | } 84 | 85 | var tests = []struct { 86 | handler []ContextHandler 87 | restrictHandler RestrictHandler 88 | expectedData interface{} 89 | }{ 90 | // Test 0: 91 | // Check if chain works 92 | { 93 | handler: []ContextHandler{ 94 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 95 | return context.WithValue(ctx, key, "data") 96 | }, 97 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 98 | response.Data = ctx.Value(key).(string) 99 | return ctx 100 | }, 101 | }, 102 | expectedData: "data", 103 | }, 104 | } 105 | 106 | // run all tests 107 | for index, test := range tests { 108 | // recorder and request to simulate test 109 | recorder := httptest.NewRecorder() 110 | request, err := http.NewRequest("POST", "", nil) 111 | 112 | // build chain 113 | handlerChain := RequestChainContextHandler(responseHandler, test.handler...) 114 | handlerChain(recorder, request, nil) 115 | 116 | // check result 117 | var responseBody map[string]interface{} 118 | json.Unmarshal([]byte(recorder.Body.String()), &responseBody) 119 | if err != nil || responseBody["data"] != test.expectedData { 120 | t.Errorf("Test %v failed: expected data is not equals response.data; expected: %v; response: %v", index, test.expectedData, responseBody["data"]) 121 | } 122 | } 123 | } 124 | 125 | func TestContextFailHandler(t *testing.T) { 126 | var tests = []struct { 127 | handler []ContextHandler 128 | restrictHandler RestrictContextHandler 129 | expectedStatus int 130 | checkResponseOfEveryHandler bool 131 | }{ 132 | // Test 0: 133 | // Check expected code is 500 134 | { 135 | handler: []ContextHandler{ 136 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 137 | response.Status.Code = http.StatusOK 138 | return ctx 139 | }, 140 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 141 | response.Status.Code = http.StatusInternalServerError 142 | return ctx 143 | }, 144 | // This handler should not run! 145 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 146 | response.Status.Code = http.StatusOK 147 | return ctx 148 | }, 149 | }, 150 | restrictHandler: func(ctx context.Context, _ *Response, _ *http.Request, _ httprouter.Params) (context.Context, bool) { 151 | return ctx, true 152 | }, 153 | expectedStatus: 500, 154 | checkResponseOfEveryHandler: true, 155 | }, 156 | 157 | // Test 1: 158 | // Check expected code == 200 159 | { 160 | handler: []ContextHandler{ 161 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 162 | response.Status.Code = http.StatusOK 163 | return ctx 164 | }, 165 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 166 | response.Status.Code = http.StatusInternalServerError 167 | return ctx 168 | }, 169 | // This handler should not run! 170 | func(ctx context.Context, response *Response, _ *http.Request, _ httprouter.Params) context.Context { 171 | response.Status.Code = http.StatusOK 172 | return ctx 173 | }, 174 | }, 175 | restrictHandler: func(ctx context.Context, _ *Response, _ *http.Request, _ httprouter.Params) (context.Context, bool) { 176 | return ctx, true 177 | }, 178 | expectedStatus: 200, 179 | checkResponseOfEveryHandler: false, 180 | }, 181 | } 182 | 183 | // run all tests 184 | for index, test := range tests { 185 | // recorder and request to simulate test 186 | recorder := httptest.NewRecorder() 187 | request, err := http.NewRequest("POST", "", nil) 188 | 189 | // build chain 190 | handlerChain := RestrictedRequestChainContextHandlerWithResponseCheck(test.checkResponseOfEveryHandler, test.restrictHandler, responseHandler, test.handler...) 191 | handlerChain(recorder, request, nil) 192 | 193 | // check result 194 | var responseBody map[string]interface{} 195 | json.Unmarshal([]byte(recorder.Body.String()), &responseBody) 196 | if err != nil || int(responseBody["status"].(float64)) != test.expectedStatus { 197 | t.Errorf("Test %v failed: expected data is not equals response.status.code; expected: %v; response: %v", index, test.expectedStatus, responseBody["status"]) 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /templates/jsonResponseHandler.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | 10 | "github.com/TobiEiss/goMiddlewareChain" 11 | "github.com/julienschmidt/httprouter" 12 | ) 13 | 14 | // JSONContentType The default content-type 15 | const JSONContentType = "application/json" 16 | 17 | // NotFoundResponseHandler handle the default 404 errors with JSONResponseHandler 18 | func NotFoundResponseHandler(writer http.ResponseWriter, request *http.Request) { 19 | JSONResponseHandler(&goMiddlewareChain.Response{Status: goMiddlewareChain.Status{Code: http.StatusNotFound}}, writer, request, nil) 20 | } 21 | 22 | // MethodNotAllowedResponseHandler handle the default 405 errors with JSONResponseHandler 23 | func MethodNotAllowedResponseHandler(writer http.ResponseWriter, request *http.Request) { 24 | JSONResponseHandler(&goMiddlewareChain.Response{Status: goMiddlewareChain.Status{Code: http.StatusMethodNotAllowed}}, writer, request, nil) 25 | } 26 | 27 | // PanicHandler handle all crashes with a proper JSONResponseHandler response 28 | func PanicHandler(writer http.ResponseWriter, request *http.Request, p interface{}) { 29 | JSONResponseHandler(&goMiddlewareChain.Response{Status: goMiddlewareChain.Status{Code: http.StatusInternalServerError}}, writer, request, nil) 30 | } 31 | 32 | // JSONResponseHandler wrap the response in a standard json structure 33 | func JSONResponseHandler(response *goMiddlewareChain.Response, writer http.ResponseWriter, request *http.Request, params httprouter.Params) { 34 | 35 | if response.Status.Code == 0 { 36 | response.Status.Code = http.StatusInternalServerError 37 | } 38 | 39 | if response.Status.Message == "" { 40 | response.Status.Message = http.StatusText(response.Status.Code) 41 | } 42 | 43 | // set proper response headers 44 | writer.Header().Set("Content-Type", JSONContentType) 45 | writer.WriteHeader(int(response.Status.Code)) 46 | 47 | // create default struct 48 | body := make(map[string]interface{}) 49 | 50 | // set response body 51 | body["status"] = response.Status.Code 52 | body["msg"] = response.Status.Message 53 | 54 | if response.Data != nil { 55 | body["data"] = response.Data 56 | } 57 | 58 | // marshal to json and send 59 | responseByte, err := json.Marshal(body) 60 | if err != nil { 61 | log.Println("Can't marshal response-body", err) 62 | } 63 | 64 | fmt.Fprintf(writer, "%s", responseByte) 65 | } 66 | 67 | // JSONResponseContextHandler wrap the response in a standard json structure 68 | func JSONResponseContextHandler(ctx context.Context, response *goMiddlewareChain.Response, writer http.ResponseWriter, request *http.Request, params httprouter.Params) context.Context { 69 | JSONResponseHandler(response, writer, request, params) 70 | return ctx 71 | } 72 | -------------------------------------------------------------------------------- /templates/logHandler.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "context" 5 | "log/syslog" 6 | "net/http" 7 | 8 | "log" 9 | 10 | "github.com/TobiEiss/goMiddlewareChain" 11 | "github.com/julienschmidt/httprouter" 12 | ) 13 | 14 | // LogHandler log requests. 15 | // Use this handler to log every call to console. 16 | func LogHandler(response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) { 17 | log.Println(request.RemoteAddr, "-", request.Method, "-", request.Host, "-", request.URL, "-", request.Header) 18 | } 19 | 20 | // LogContextHandler log requests 21 | // Use this context-handler to log every call to console. 22 | func LogContextHandler(ctx context.Context, response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) context.Context { 23 | LogHandler(response, request, params) 24 | return ctx 25 | } 26 | 27 | // LoggerHandler holds the logger-instance 28 | type LoggerHandler struct { 29 | Writer *syslog.Writer 30 | } 31 | 32 | // NewLoggerHandler creates a new LoggerHandler 33 | func NewLoggerHandler(writer *syslog.Writer) *LoggerHandler { 34 | return &LoggerHandler{Writer: writer} 35 | } 36 | 37 | // LoggerContextKey a key to map the logger 38 | var LoggerContextKey = goMiddlewareChain.ContextKey{Key: ""} 39 | 40 | // Handle is the handler for the LoggerHandler 41 | func (handler *LoggerHandler) Handle(ctx context.Context, response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) context.Context { 42 | return context.WithValue(ctx, LoggerContextKey, handler) 43 | } 44 | -------------------------------------------------------------------------------- /templates/pongHandler.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/TobiEiss/goMiddlewareChain" 7 | "github.com/julienschmidt/httprouter" 8 | ) 9 | 10 | // Ping route should just respond with pong 11 | func Ping(response *goMiddlewareChain.Response, request *http.Request, params httprouter.Params) { 12 | response.Data = "PONG" 13 | response.Status.Code = http.StatusOK 14 | } 15 | --------------------------------------------------------------------------------