{"id":13367437,"url":"https://github.com/Go-ozzo/ozzo-routing","last_synced_at":"2025-03-12T18:32:43.611Z","repository":{"id":1971045,"uuid":"45007925","full_name":"go-ozzo/ozzo-routing","owner":"go-ozzo","description":"An extremely fast Go (golang) HTTP router that supports regular expression route matching. Comes with full support for building RESTful APIs.","archived":false,"fork":false,"pushed_at":"2022-05-08T09:14:18.000Z","size":223,"stargazers_count":454,"open_issues_count":12,"forks_count":51,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-02-26T21:53:45.432Z","etag":null,"topics":["framework","go","golang","ozzo","router"],"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/go-ozzo.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":null,"security":null,"support":null}},"created_at":"2015-10-27T01:03:14.000Z","updated_at":"2025-01-09T04:20:50.000Z","dependencies_parsed_at":"2022-08-08T16:00:02.086Z","dependency_job_id":null,"html_url":"https://github.com/go-ozzo/ozzo-routing","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-ozzo%2Fozzo-routing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-ozzo%2Fozzo-routing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-ozzo%2Fozzo-routing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-ozzo%2Fozzo-routing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/go-ozzo","download_url":"https://codeload.github.com/go-ozzo/ozzo-routing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243271665,"owners_count":20264505,"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":["framework","go","golang","ozzo","router"],"created_at":"2024-07-30T00:01:48.987Z","updated_at":"2025-03-12T18:32:43.304Z","avatar_url":"https://github.com/go-ozzo.png","language":"Go","funding_links":[],"categories":["XML"],"sub_categories":["路由"],"readme":"# ozzo-routing\n\n[![GoDoc](https://godoc.org/github.com/go-ozzo/ozzo-routing?status.png)](http://godoc.org/github.com/go-ozzo/ozzo-routing)\n[![Build Status](https://travis-ci.org/go-ozzo/ozzo-routing.svg?branch=master)](https://travis-ci.org/go-ozzo/ozzo-routing)\n[![Coverage Status](https://coveralls.io/repos/github/go-ozzo/ozzo-routing/badge.svg?branch=master)](https://coveralls.io/github/go-ozzo/ozzo-routing?branch=master)\n[![Go Report](https://goreportcard.com/badge/github.com/go-ozzo/ozzo-routing)](https://goreportcard.com/report/github.com/go-ozzo/ozzo-routing)\n\n**You may consider using [go-rest-api](https://github.com/qiangxue/go-rest-api) to jumpstart your new RESTful applications with ozzo-routing.**\n\n## Description\n\nozzo-routing is a Go package that provides high performance and powerful HTTP routing capabilities for Web applications.\nIt has the following features:\n\n* middleware pipeline architecture, similar to that of the [Express framework](http://expressjs.com).\n* extremely fast request routing with zero dynamic memory allocation (the performance is comparable to that of [httprouter](https://github.com/julienschmidt/httprouter) and\n[gin](https://github.com/gin-gonic/gin), see the [performance comparison below](#benchmarks))\n* modular code organization through route grouping\n* flexible URL path matching, supporting URL parameters and regular expressions\n* URL creation according to the predefined routes\n* compatible with `http.Handler` and `http.HandlerFunc`\n* ready-to-use handlers sufficient for building RESTful APIs\n* graceful shutdown\n\nIf you are using [fasthttp](https://github.com/valyala/fasthttp), you may use a similar routing package [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing) which is adapted from ozzo-routing.\n\n## Requirements\n\nGo 1.13 or above.\n\n## Installation\n\nIn your Go project using `go mod`, run the following command to install the package:\n\n```\ngo get github.com/go-ozzo/ozzo-routing/v2\n```\n\n## Getting Started\n\nFor a complete RESTful application boilerplate based on ozzo-routing, please refer to the [golang-restful-starter-kit](https://github.com/qiangxue/golang-restful-starter-kit). Below we describe how to create a simple REST API using ozzo-routing.\n\nCreate a `server.go` file with the following content:\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"github.com/go-ozzo/ozzo-routing/v2\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/access\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/slash\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/content\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/fault\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/file\"\n)\n\nfunc main() {\n\trouter := routing.New()\n\n\trouter.Use(\n\t\t// all these handlers are shared by every route\n\t\taccess.Logger(log.Printf),\n\t\tslash.Remover(http.StatusMovedPermanently),\n\t\tfault.Recovery(log.Printf),\n\t)\n\n\t// serve RESTful APIs\n\tapi := router.Group(\"/api\")\n\tapi.Use(\n\t\t// these handlers are shared by the routes in the api group only\n\t\tcontent.TypeNegotiator(content.JSON, content.XML),\n\t)\n\tapi.Get(\"/users\", func(c *routing.Context) error {\n\t\treturn c.Write(\"user list\")\n\t})\n\tapi.Post(\"/users\", func(c *routing.Context) error {\n\t\treturn c.Write(\"create a new user\")\n\t})\n\tapi.Put(`/users/\u003cid:\\d+\u003e`, func(c *routing.Context) error {\n\t\treturn c.Write(\"update user \" + c.Param(\"id\"))\n\t})\n\n\t// serve index file\n\trouter.Get(\"/\", file.Content(\"ui/index.html\"))\n\t// serve files under the \"ui\" subdirectory\n\trouter.Get(\"/*\", file.Server(file.PathMap{\n\t\t\"/\": \"/ui/\",\n\t}))\n\n\thttp.Handle(\"/\", router)\n\thttp.ListenAndServe(\":8080\", nil)\n}\n```\n\nCreate an HTML file `ui/index.html` with any content.\n\nNow run the following command to start the Web server:\n\n```\ngo run server.go\n```\n\nYou should be able to access URLs such as `http://localhost:8080`, `http://localhost:8080/api/users`.\n\n\n### Routes\n\nozzo-routing works by building a routing table in a router and then dispatching HTTP requests to the matching handlers \nfound in the routing table. An intuitive illustration of a routing table is as follows:\n\n\nRoutes              |  Handlers\n--------------------|-----------------\n`GET /users`        |  m1, m2, h1, ...\n`POST /users`       |  m1, m2, h2, ...\n`PUT /users/\u003cid\u003e`   |  m1, m2, h3, ...\n`DELETE /users/\u003cid\u003e`|  m1, m2, h4, ...\n\n\nFor an incoming request `GET /users`, the first route would match and the handlers m1, m2, and h1 would be executed.\nIf the request is `PUT /users/123`, the third route would match and the corresponding handlers would be executed.\nNote that the token `\u003cid\u003e` can match any number of non-slash characters and the matching part can be accessed as \na path parameter value in the handlers.\n\n**If an incoming request matches multiple routes in the table, the route added first to the table will take precedence.\nAll other matching routes will be ignored.**\n\nThe actual implementation of the routing table uses a variant of the radix tree data structure, which makes the routing\nprocess as fast as working with a hash table, thanks to the inspiration from [httprouter](https://github.com/julienschmidt/httprouter).\n\nTo add a new route and its handlers to the routing table, call the `To` method like the following:\n  \n```go\nrouter := routing.New()\nrouter.To(\"GET\", \"/users\", m1, m2, h1)\nrouter.To(\"POST\", \"/users\", m1, m2, h2)\n```\n\nYou can also use shortcut methods, such as `Get`, `Post`, `Put`, etc., which are named after the HTTP method names:\n \n```go\nrouter.Get(\"/users\", m1, m2, h1)\nrouter.Post(\"/users\", m1, m2, h2)\n```\n\nIf you have multiple routes with the same URL path but different HTTP methods, like the above example, you can \nchain them together as follows,\n\n```go\nrouter.Get(\"/users\", m1, m2, h1).Post(m1, m2, h2)\n```\n\nIf you want to use the same set of handlers to handle the same URL path but different HTTP methods, you can take\nthe following shortcut:\n\n```go\nrouter.To(\"GET,POST\", \"/users\", m1, m2, h)\n```\n\nA route may contain parameter tokens which are in the format of `\u003cname:pattern\u003e`, where `name` stands for the parameter\nname, and `pattern` is a regular expression which the parameter value should match. A token `\u003cname\u003e` is equivalent\nto `\u003cname:[^/]*\u003e`, i.e., it matches any number of non-slash characters. At the end of a route, an asterisk character\ncan be used to match any number of arbitrary characters. Below are some examples:\n\n* `/users/\u003cusername\u003e`: matches `/users/admin`\n* `/users/accnt-\u003cid:\\d+\u003e`: matches `/users/accnt-123`, but not `/users/accnt-admin`\n* `/users/\u003cusername\u003e/*`: matches `/users/admin/profile/address`\n\nWhen a URL path matches a route, the matching parameters on the URL path can be accessed via `Context.Param()`:\n\n```go\nrouter := routing.New()\n\nrouter.Get(\"/users/\u003cusername\u003e\", func (c *routing.Context) error {\n\tfmt.Fprintf(c.Response, \"Name: %v\", c.Param(\"username\"))\n\treturn nil\n})\n```\n\n\n### Route Groups\n\nRoute group is a way of grouping together the routes which have the same route prefix. The routes in a group also\nshare the same handlers that are registered with the group via its `Use` method. For example,\n\n```go\nrouter := routing.New()\napi := router.Group(\"/api\")\napi.Use(m1, m2)\napi.Get(\"/users\", h1).Post(h2)\napi.Put(\"/users/\u003cid\u003e\", h3).Delete(h4)\n```\n\nThe above `/api` route group establishes the following routing table:\n\n\nRoutes                  |  Handlers\n------------------------|-------------\n`GET /api/users`        |  m1, m2, h1, ...\n`POST /api/users`       |  m1, m2, h2, ...\n`PUT /api/users/\u003cid\u003e`   |  m1, m2, h3, ...\n`DELETE /api/users/\u003cid\u003e`|  m1, m2, h4, ...\n\n\nAs you can see, all these routes have the same route prefix `/api` and the handlers `m1` and `m2`. In other similar\nrouting frameworks, the handlers registered with a route group are also called *middlewares*.\n\nRoute groups can be nested. That is, a route group can create a child group by calling the `Group()` method. The router\nserves as the top level route group. A child group inherits the handlers registered with its parent group. For example, \n\n```go\nrouter := routing.New()\nrouter.Use(m1)\n\napi := router.Group(\"/api\")\napi.Use(m2)\n\nusers := api.Group(\"/users\")\nusers.Use(m3)\nusers.Put(\"/\u003cid\u003e\", h1)\n```\n\nBecause the router serves as the parent of the `api` group which is the parent of the `users` group, \nthe `PUT /api/users/\u003cid\u003e` route is associated with the handlers `m1`, `m2`, `m3`, and `h1`.\n\n\n### Router\n\nRouter manages the routing table and dispatches incoming requests to appropriate handlers. A router instance is created\nby calling the `routing.New()` method.\n\nBecause `Router` implements the `http.Handler` interface, it can be readily used to serve subtrees on existing Go servers.\nFor example,\n\n```go\nrouter := routing.New()\nhttp.Handle(\"/\", router)\nhttp.ListenAndServe(\":8080\", nil)\n```\n\n\n### Handlers\n\nA handler is a function with the signature `func(*routing.Context) error`. A handler is executed by the router if\nthe incoming request URL path matches the route that the handler is associated with. Through the `routing.Context` \nparameter, you can access the request information in handlers.\n\nA route may be associated with multiple handlers. These handlers will be executed in the order that they are registered\nto the route. The execution sequence can be terminated in the middle using one of the following two methods:\n\n* A handler returns an error: the router will skip the rest of the handlers and handle the returned error.\n* A handler calls `Context.Abort()`: the router will simply skip the rest of the handlers. There is no error to be handled.\n \nA handler can call `Context.Next()` to explicitly execute the rest of the unexecuted handlers and take actions after\nthey finish execution. For example, a response compression handler may start the output buffer, call `Context.Next()`,\nand then compress and send the output to response.\n\n\n### Context\n\nFor each incoming request, a `routing.Context` object is populated with the request information and passed through\nthe handlers that need to handle the request. Handlers can get the request information via `Context.Request` and\nsend a response back via `Context.Response`. The `Context.Param()` method allows handlers to access the URL path\nparameters that match the current route.\n\nUsing `Context.Get()` and `Context.Set()`, handlers can share data between each other. For example, an authentication\nhandler can store the authenticated user identity by calling `Context.Set()`, and other handlers can retrieve back\nthe identity information by calling `Context.Get()`.\n\n\n### Reading Request Data\n\nContext provides a few shortcut methods to read query parameters. The `Context.Query()`  method returns\nthe named URL query parameter value; the `Context.PostForm()` method returns the named parameter value in the POST or\nPUT body parameters; and the `Context.Form()` method returns the value from either POST/PUT or URL query parameters.\n\nThe `Context.Read()` method supports reading data from the request body and populating it into an object.\nThe method will check the `Content-Type` HTTP header and parse the body data as the corresponding format.\nFor example, if `Content-Type` is `application/json`, the request body will be parsed as JSON data.\nThe public fields in the object being populated will receive the parsed data if the data contains the same named fields.\nFor example,\n\n```go\nfunc foo(c *routing.Context) error {\n    data := \u0026struct{\n        A string\n        B bool\n    }{}\n\n    // assume the body data is: {\"A\":\"abc\", \"B\":true}\n    // data will be populated as: {A: \"abc\", B: true}\n    if err := c.Read(\u0026data); err != nil {\n        return err\n    }\n}\n```\n\nBy default, `Context` supports reading data that are in JSON, XML, form, and multipart-form data.\nYou may modify `routing.DataReaders` to add support for other data formats.\n\nNote that when the data is read as form data, you may use struct tag named `form` to customize\nthe name of the corresponding field in the form data. The form data reader also supports populating\ndata into embedded objects which are either named or anonymous.\n\n### Writing Response Data\n\nThe `Context.Write()` method can be used to write data of arbitrary type to the response.\nBy default, if the data being written is neither a string nor a byte array, the method will\nwill call `fmt.Fprint()` to write the data into the response.\n\nYou can call `Context.SetWriter()` to replace the default data writer with a customized one.\nFor example, the `content.TypeNegotiator` will negotiate the content response type and set the data\nwriter with an appropriate one.\n\n### Error Handling\n\nA handler may return an error indicating some erroneous condition. Sometimes, a handler or the code it calls may cause\na panic. Both should be handled properly to ensure best user experience. It is recommended that you use \nthe `fault.Recover` handler or a similar error handler to handle these errors.\n\nIf an error is not handled by any handler, the router will handle it by calling its `handleError()` method which\nsimply sets an appropriate HTTP status code and writes the error message to the response.\n\nWhen an incoming request has no matching route, the router will call the handlers registered via the `Router.NotFound()`\nmethod. All the handlers registered via `Router.Use()` will also be called in advance. By default, the following two\nhandlers are registered with `Router.NotFound()`:\n\n* `routing.MethodNotAllowedHandler`: a handler that sends an `Allow` HTTP header indicating the allowed HTTP methods for a requested URL\n* `routing.NotFoundHandler`: a handler triggering 404 HTTP error\n\n## Serving Static Files\n\nStatic files can be served with the help of `file.Server` and `file.Content` handlers. The former serves files\nunder the specified directories, while the latter serves the content of a single file. For example,\n\n```go\nimport (\n\t\"github.com/go-ozzo/ozzo-routing/v2\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/file\"\n)\n\nrouter := routing.NewRouter()\n\n// serve index file\nrouter.Get(\"/\", file.Content(\"ui/index.html\"))\n// serve files under the \"ui\" subdirectory\nrouter.Get(\"/*\", file.Server(file.PathMap{\n\t\"/\": \"/ui/\",\n}))\n```\n\n## Handlers\n\nozzo-routing comes with a few commonly used handlers in its subpackages:\n\nHandler name \t\t\t\t\t| Description\n--------------------------------|--------------------------------------------\n[access.Logger](https://godoc.org/github.com/go-ozzo/ozzo-routing/access) | records an entry for every incoming request\n[auth.Basic](https://godoc.org/github.com/go-ozzo/ozzo-routing/auth) | provides authentication via HTTP Basic\n[auth.Bearer](https://godoc.org/github.com/go-ozzo/ozzo-routing/auth) | provides authentication via HTTP Bearer\n[auth.Query](https://godoc.org/github.com/go-ozzo/ozzo-routing/auth) | provides authentication via token-based query parameter\n[auth.JWT](https://godoc.org/github.com/go-ozzo/ozzo-routing/auth) | provides JWT-based authentication\n[content.TypeNegotiator](https://godoc.org/github.com/go-ozzo/ozzo-routing/content) | supports content negotiation by response types\n[content.LanguageNegotiator](https://godoc.org/github.com/go-ozzo/ozzo-routing/content) | supports content negotiation by accepted languages\n[cors.Handler](https://godoc.org/github.com/go-ozzo/ozzo-routing/cors) | implements the CORS (Cross Origin Resource Sharing) specification from the W3C\n[fault.Recovery](https://godoc.org/github.com/go-ozzo/ozzo-routing/fault) | recovers from panics and handles errors returned by handlers\n[fault.PanicHandler](https://godoc.org/github.com/go-ozzo/ozzo-routing/fault) | recovers from panics happened in the handlers\n[fault.ErrorHandler](https://godoc.org/github.com/go-ozzo/ozzo-routing/fault) | handles errors returned by handlers by writing them in an appropriate format to the response\n[file.Server](https://godoc.org/github.com/go-ozzo/ozzo-routing/file) | serves the files under the specified folder as response content\n[file.Content](https://godoc.org/github.com/go-ozzo/ozzo-routing/file) | serves the content of the specified file as the response\n[slash.Remover](https://godoc.org/github.com/go-ozzo/ozzo-routing/slash) | removes the trailing slashes from the request URL and redirects to the proper URL\n\nThe following code shows how these handlers may be used:\n\n```go\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"github.com/go-ozzo/ozzo-routing/v2\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/access\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/slash\"\n\t\"github.com/go-ozzo/ozzo-routing/v2/fault\"\n)\n\nrouter := routing.New()\n\nrouter.Use(\n\taccess.Logger(log.Printf),\n\tslash.Remover(http.StatusMovedPermanently),\n\tfault.Recovery(log.Printf),\n)\n\n...\n```\n\n### Third-party Handlers\n\n\nThe following third-party handlers are specifically designed for ozzo-routing:\n\nHandler name \t\t\t\t\t| Description\n--------------------------------|--------------------------------------------\n[jwt.JWT](https://github.com/vvv-v13/ozzo-jwt) | supports JWT Authorization\n\n\nozzo-routing also provides adapters to support using third-party `http.HandlerFunc` or `http.Handler` handlers. \nFor example,\n\n```go\nrouter := routing.New()\n\n// using http.HandlerFunc\nrouter.Use(routing.HTTPHandlerFunc(http.NotFound))\n\n// using http.Handler\nrouter.Use(routing.HTTPHandler(http.NotFoundHandler))\n```\n\n## 3rd-Party Extensions and Code Examples\n\n* [Simple Standard Service Endpoints (SE4)](https://github.com/jdamick/ozzo-se4)\n* [ozzo examples](https://github.com/marshyski/go-ozzo-examples)\n\n## Benchmarks\n\n*Last updated on Jan 6, 2017*\n\nOzzo-routing is very fast, thanks to the radix tree data structure and the usage of `sync.Pool` (the idea was\noriginally from HttpRouter and Gin). The following table (by running [go-http-routing-benchmark](https://github.com/qiangxue/go-http-routing-benchmark))\nshows how ozzo-routing compares with Gin, HttpRouter, and Martini in performance.\n\n```\nBenchmarkOzzo_GithubAll                    50000             37989 ns/op               0 B/op          0 allocs/op\nBenchmarkEcho_GithubAll                    20000             91003 ns/op            6496 B/op        203 allocs/op\nBenchmarkGin_GithubAll                     50000             26717 ns/op               0 B/op          0 allocs/op\nBenchmarkHttpRouter_GithubAll              50000             36052 ns/op           13792 B/op        167 allocs/op\nBenchmarkMartini_GithubAll                   300           4162283 ns/op          228216 B/op       2483 allocs/op\n\nBenchmarkOzzo_GPlusAll                   1000000              1732 ns/op               0 B/op          0 allocs/op\nBenchmarkEcho_GPlusAll                    300000              4523 ns/op             416 B/op         13 allocs/op\nBenchmarkGin_GPlusAll                    1000000              1171 ns/op               0 B/op          0 allocs/op\nBenchmarkHttpRouter_GPlusAll             1000000              1533 ns/op             640 B/op         11 allocs/op\nBenchmarkMartini_GPlusAll                  20000             75634 ns/op           14448 B/op        165 allocs/op\n\nBenchmarkOzzo_ParseAll                    500000              3318 ns/op               0 B/op          0 allocs/op\nBenchmarkEcho_ParseAll                    200000              7336 ns/op             832 B/op         26 allocs/op\nBenchmarkGin_ParseAll                    1000000              2075 ns/op               0 B/op          0 allocs/op\nBenchmarkHttpRouter_ParseAll             1000000              2034 ns/op             640 B/op         16 allocs/op\nBenchmarkMartini_ParseAll                  10000            122002 ns/op           25600 B/op        276 allocs/op\n```\n\n## Credits\n\nozzo-routing has referenced many popular routing frameworks, including [Express](http://expressjs.com/), \n[Martini](https://github.com/go-martini/martini), [httprouter](https://github.com/julienschmidt/httprouter), and\n[gin](https://github.com/gin-gonic/gin). \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGo-ozzo%2Fozzo-routing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGo-ozzo%2Fozzo-routing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGo-ozzo%2Fozzo-routing/lists"}