{"id":13764097,"url":"https://github.com/InVisionApp/rye","last_synced_at":"2025-05-10T17:31:38.207Z","repository":{"id":57487972,"uuid":"70187842","full_name":"InVisionApp/rye","owner":"InVisionApp","description":"A tiny http middleware for Golang with added handlers for common needs.","archived":true,"fork":false,"pushed_at":"2024-12-23T17:57:11.000Z","size":2898,"stargazers_count":100,"open_issues_count":0,"forks_count":13,"subscribers_count":201,"default_branch":"master","last_synced_at":"2025-04-12T15:04:54.947Z","etag":null,"topics":["basic-auth","chain","golang","http-handler","jwt-middleware","middleware","middleware-handlers","rye","statsd"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/InVisionApp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-10-06T19:51:59.000Z","updated_at":"2025-03-06T13:04:03.000Z","dependencies_parsed_at":"2024-01-08T16:08:46.929Z","dependency_job_id":"baeafd29-f968-47dd-b0fb-b02f71c71feb","html_url":"https://github.com/InVisionApp/rye","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InVisionApp%2Frye","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InVisionApp%2Frye/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InVisionApp%2Frye/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InVisionApp%2Frye/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/InVisionApp","download_url":"https://codeload.github.com/InVisionApp/rye/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253453337,"owners_count":21911080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["basic-auth","chain","golang","http-handler","jwt-middleware","middleware","middleware-handlers","rye","statsd"],"created_at":"2024-08-03T15:01:13.699Z","updated_at":"2025-05-10T17:31:38.194Z","avatar_url":"https://github.com/InVisionApp.png","language":"Go","readme":"| :warning: This project is no longer actively supported.\n| ---\n\n[![LICENSE](https://img.shields.io/badge/license-MIT-orange.svg)](LICENSE)\n[![Golang](https://img.shields.io/badge/Golang-v1.7-blue.svg)](https://golang.org/dl/)\n[![Godocs](https://img.shields.io/badge/golang-documentation-blue.svg)](https://godoc.org/github.com/InVisionApp/rye)\n[![Go Report Card](https://goreportcard.com/badge/github.com/InVisionApp/rye)](https://goreportcard.com/report/github.com/InVisionApp/rye)\n[![Travis Build Status](https://travis-ci.com/InVisionApp/rye.svg?token=qgpSBc6cjHgbnjqC45af\u0026branch=master)](https://travis-ci.com/InVisionApp/rye)\n[![codecov](https://codecov.io/gh/InVisionApp/rye/branch/master/graph/badge.svg?token=hhqA1l88kx)](https://codecov.io/gh/InVisionApp/rye)\n\n\u003cimg align=\"right\" src=\"images/rye_logo_invision_pink.png\"\u003e\n\u003cimg align=\"right\" src=\"images/rye-gopher.svg\" width=\"200\"\u003e\n\n# rye\nA simple library to support http services. Currently, **rye** provides a middleware handler which can be used to chain http handlers together while providing statsd metrics for use with DataDog or other logging aggregators. In addition, **rye** comes with various pre-built middleware handlers for enabling functionality such as CORS and rate/CIDR limiting.\n\n## Setup\nIn order to use **rye**, you should vendor it and the **statsd** client within your project.\n\n```sh \ngovendor fetch github.com/InVisionApp/rye\ngovendor fetch github.com/cactus/go-statsd-client/statsd\n```\n\n## Why another middleware lib?\n\n* `rye` is *tiny* - the core lib is ~143 lines of code (including comments)!\n* Each middleware gets statsd metrics tracking for free including an overall error counter\n* We wanted to have an easy way to say “run these two middlewares on this endpoint, but only one middleware on this endpoint” \n    * Of course, this is doable with negroni and gorilla-mux, but you’d have to use a subrouter with gorilla, which tends to end up in more code\n* Bundled helper methods for standardising JSON response messages\n* Unified way for handlers and middlewares to return more detailed responses via the `rye.Response` struct (if they chose to do so).\n* Pre-built middlewares for things like CORS support\n\n## Example\n\nYou can run an example locally to give it a try. The code for the example is [here](example/rye_example.go)! \n\n```sh\ncd example\ngo run rye_example.go\n```\n\n## Writing custom middleware handlers\n\nBegin by importing the required libraries:\n\n```go\nimport (\n    \"github.com/cactus/go-statsd-client/statsd\"\n    \"github.com/InVisionApp/rye\"\n)\n```\n\nCreate a statsd client (if desired) and create a rye Config in order to pass in optional dependencies:\n\n```go\nconfig := \u0026rye.Config{\n    Statter:  statsdClient,\n    StatRate: DEFAULT_STATSD_RATE,\n}\n```\n\nCreate a middleware handler. The purpose of the Handler is to keep Config and to provide an interface for chaining http handlers.\n\n```go\nmiddlewareHandler := rye.NewMWHandler(config)\n```\n\nSet up any global handlers by using the `Use()` method. Global handlers get pre-pended to the list of your handlers for EVERY endpoint.\nThey are bound to the MWHandler struct. Therefore, you could set up multiple MWHandler structs if you want to have different collections\nof global handlers.\n\n```go\nmiddlewareHandler.Use(middleware_routelogger)\n```\n\nBuild your http handlers using the Handler type from the **rye** package.\n\n```go\ntype Handler func(w http.ResponseWriter, r *http.Request) *rye.Response\n```\n\nHere are some example (custom) handlers:\n\n```go\nfunc homeHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {\n    fmt.Fprint(rw, \"Refer to README.md for auth-api API usage\")\n    return nil\n}\n\nfunc middlewareFirstHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {\n    fmt.Fprint(rw, \"This handler fires first.\")\n    return nil\n}\n\nfunc errorHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {\n    return \u0026rye.Response{\n        StatusCode: http.StatusInternalServerError,\n        Err:        errors.New(message),\n    }\n}\n```\n\nFinally, to setup your handlers in your API (Example shown using [Gorilla](https://github.com/gorilla/mux)):\n```go\nroutes := mux.NewRouter().StrictSlash(true)\n\nroutes.Handle(\"/\", middlewareHandler.Handle([]rye.Handler{\n    a.middlewareFirstHandler,\n    a.homeHandler,\n})).Methods(\"GET\")\n\nlog.Infof(\"API server listening on %v\", ListenAddress)\n\nsrv := \u0026http.Server{\n    Addr:    ListenAddress,\n    Handler: routes,\n}\n\nsrv.ListenAndServe()\n```\n\n## Statsd Generated by Rye\n\nRye comes with built-in configurable `statsd` statistics that you could record to your favorite monitoring system. To configure that, you'll need to set up a `Statter` based on the `github.com/cactus/go-statsd-client` and set it in your instantiation of `MWHandler` through the `rye.Config`.\n\nWhen a middleware is called, it's timing is recorded and a counter is recorded associated directly with the http status code returned during the call. Additionally, an `errors` counter is also sent to the statter which allows you to count any errors that occur with a code equaling or above 500. \n\nExample: If you have a middleware handler you've created with a method named `loginHandler`, successful calls to that will be recorded to `handlers.loginHandler.2xx`. Additionally you'll receive stats such as `handlers.loginHandler.400` or `handlers.loginHandler.500`. You also will receive an increase in the `errors` count.\n\n_If you're sending your logs into a system such as DataDog, be aware that your stats from Rye can have prefixes such as `statsd.my-service.my-k8s-cluster.handlers.loginHandler.2xx` or even `statsd.my-service.my-k8s-cluster.errors`. Just keep in mind your stats could end up in the destination sink system with prefixes._\n\n## Using with Golang 1.7 Context\n\nWith Golang 1.7, a new feature has been added that supports a request specific context. This is a great feature that Rye supports out-of-the-box. The tricky part of this is how the context is modified on the request. In Golang, the Context is always available on a Request through `http.Request.Context()`. Great! However, if you want to add key/value pairs to the context, you will have to add the context to the request before it gets passed to the next Middleware. To support this, the `rye.Response` has a property called `Context`. This property takes a properly created context (pulled from the `request.Context()` function. When you return a `rye.Response` which has `Context`, the **rye** library will craft a new Request and make sure that the next middleware receives that request. \n\nHere's the details of creating a middleware with a proper `Context`. You must first pull from the current request `Context`. In the example below, you see `ctx := r.Context()`. That pulls the current context. Then, you create a NEW context with your additional context key/value. Finally, you return `\u0026rye.Response{Context:ctx}`\n\n```go\nfunc addContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {\n    // Retrieve the request's context\n    ctx := r.Context()\n\n    // Create a NEW context\n    ctx = context.WithValue(ctx,\"CONTEXT_KEY\",\"my context value\")\n\n    // Return that in the Rye response \n    // Rye will add it to the Request to \n    // pass to the next middleware\n    return \u0026rye.Response{Context:ctx}\n}\n```\nNow in a later middleware, you can easily retrieve the value you set!\n```go\nfunc getContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {\n    // Retrieving the value is easy!\n    myVal := r.Context().Value(\"CONTEXT_KEY\")\n\n    // Log it to the server log?\n    log.Infof(\"Context Value: %v\", myVal)\n\n    return nil\n}\n```\nFor another simple example, look in the [JWT middleware](middleware_jwt.go) - it adds the JWT into the context for use by other middlewares. It uses the `CONTEXT_JWT` key to push the JWT token into the `Context`.\n\n## Using built-in middleware handlers\n\nRye comes with various pre-built middleware handlers. Pre-built middlewares  source (and docs) can be found in the package dir following the pattern `middleware_*.go`.\n\nTo use them, specify the constructor of the middleware as one of the middleware handlers when you define your routes:\n\n```go\n// example\nroutes.Handle(\"/\", middlewareHandler.Handle([]rye.Handler{\n    rye.MiddlewareCORS(), // to use the CORS middleware (with defaults)\n    a.homeHandler,\n})).Methods(\"GET\")\n\nOR \n\nroutes.Handle(\"/\", middlewareHandler.Handle([]rye.Handler{\n    rye.NewMiddlewareCORS(\"*\", \"GET, POST\", \"X-Access-Token\"), // to use specific config when instantiating the middleware handler\n    a.homeHandler,\n})).Methods(\"GET\")\n\n```\n\n## Serving Static Files\n\nRye has the ability to add serving static files in the chain. Two handlers \nhave been provided: `StaticFilesystem` and `StaticFile`. These middlewares \nshould always be used at the end of the chain. Their configuration is \nsimply based on an absolute path on the server and possibly a skipped \npath prefix.\n\nThe use case here could be a powerful one. Rye allows you to serve a filesystem \njust as a whole or a single file. Used together you could facilitate an application \nwhich does both -\u003e fulfilling the capability to provide a single page application. \nFor example, if you had a webpack application which served static resources and \nartifacts, you would use the `StaticFilesystem` to serve those. Then you'd use \n`StaticFile` to serve the single page which refers to the single-page application \nthrough `index.html`. \n\nA full sample is provided in the `static-examples` folder. Here's a snippet from \nthe example using Gorilla:\n\n```go\n    pwd, err := os.Getwd()\n    if err != nil {\n        log.Fatalf(\"NewStaticFile: Could not get working directory.\")\n    }\n\n    routes.PathPrefix(\"/dist/\").Handler(middlewareHandler.Handle([]rye.Handler{\n        rye.MiddlewareRouteLogger(),\n        rye.NewStaticFilesystem(pwd+\"/dist/\", \"/dist/\"),\n    }))\n\n    routes.PathPrefix(\"/ui/\").Handler(middlewareHandler.Handle([]rye.Handler{\n        rye.MiddlewareRouteLogger(),\n        rye.NewStaticFile(pwd + \"/dist/index.html\"),\n    }))\n```\n\n### Middleware list\n\n| Name                       | Description                           |\n|----------------------------|---------------------------------------|\n| [Access Token](middleware_accesstoken.go)   | Provide Access Token validation   |\n| [CIDR](middleware_cidr.go) | Provide request IP whitelisting       |\n| [CORS](middleware_cors.go) | Provide CORS functionality for routes |\n| [Auth](middleware_auth.go)   | Provide Authorization header validation (basic auth, JWT)   |\n| [Route Logger](middleware_routelogger.go)   | Provide basic logging for a specific route |\n| [Static File](middleware_static_file.go) | Provides serving a single file |\n| [Static Filesystem](middleware_static_filesystem.go) | Provides serving a single file |\n\n\n### A Note on the JWT Middleware\n\nThe [JWT Middleware](middleware_auth.go) pushes the JWT token onto the Context for use by other middlewares in the chain. This is a convenience that allows any part of your middleware chain quick access to the JWT. Example usage might include a middleware that needs access to your user id or email address stored in the JWT. To access this `Context` variable, the code is very simple:\n```go\nfunc getJWTfromContext(rw http.ResponseWriter, r *http.Request) *rye.Response {\n    // Retrieving the value is easy!\n    // Just reference the rye.CONTEXT_JWT const as a key\n    myVal := r.Context().Value(rye.CONTEXT_JWT)\n\n    // Log it to the server log?\n    log.Infof(\"Context Value: %v\", myVal)\n\n    return nil\n}\n```\n\n## API\n\n### Config\nThis struct is configuration for the MWHandler. It holds references and config to dependencies such as the statsdClient.\n```go\ntype Config struct {\n    Statter  statsd.Statter\n    StatRate float32\n}\n```\n\n### MWHandler\nThis struct is the primary handler container. It holds references to the statsd client.\n```go\ntype MWHandler struct {\n    Config Config\n}\n```\n\n#### Constructor\n```go\nfunc NewMWHandler(statter statsd.Statter, statrate float32) *MWHandler\n```\n\n#### Use\nThis method prepends a global handler for every Handle method you call.\nUse this multiple times to setup global handlers for every endpoint.\nCall `Use()` for each global handler before setting up additional routes.\n```go\nfunc (m *MWHandler) Use(handlers Handler)\n```\n\n#### Handle\nThis method chains middleware handlers in order and returns a complete `http.Handler`.\n```go\nfunc (m *MWHandler) Handle(handlers []Handler) http.Handler\n```\n\n### rye.Response\nThis struct is utilized by middlewares as a way to share state; ie. a middleware can return a `*rye.Response` as a way to indicate that further middleware execution should stop (without an error) or return a hard error by setting `Err` + `StatusCode` or add to the request `Context` by returning a non-nil `Context`.\n```go\ntype Response struct {\n    Err           error\n    StatusCode    int\n    StopExecution bool\n    Context       context.Context\n}\n```\n\n### Handler\nThis type is used to define an http handler that can be chained using the MWHandler.Handle method. The `rye.Response` is from the **rye** package and has facilities to emit StatusCode, bubble up errors and/or stop further middleware execution chain.\n```go\ntype Handler func(w http.ResponseWriter, r *http.Request) *rye.Response\n```\n\n## Test stuff\nAll interfacing with the project is done via `make`. Targets exist for all primary tasks such as:\n\n- Testing: `make test` or `make testv` (for verbosity)\n- Generate: `make generate` - this generates based on vendored libraries (from $GOPATH)\n- All (test, build): `make all`\n- .. and a few others. Run `make help` to see all available targets.\n- You can also test the project in Docker (and Codeship) by running `jet steps`\n\n## Contributing\nFork the repository, write a PR and we'll consider it!\n\n## Special Thanks\nThanks go out to Justin Reyna (InVisionApp.com) for the awesome logo!\n","funding_links":[],"categories":["Web Frameworks","Web框架","中间件### 中间件","中间件","XML","Libraries for creating HTTP middlewares"],"sub_categories":["Middlewares","创建http中间件的代码库","中间件","Fail injection","中間件"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FInVisionApp%2Frye","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FInVisionApp%2Frye","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FInVisionApp%2Frye/lists"}