Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
217
README.md
217
README.md
|
@ -1,7 +1,7 @@
|
||||||
# logging
|
# logging
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/amuz.es/src/go/logging?status.png)](http://godoc.org/amuz.es/src/go/logging)
|
[![GoDoc](https://godoc.org/amuz.es/src/go/logging?status.png)](http://godoc.org/amuz.es/src/go/logging)
|
||||||
[![Go Report](https://goreportcard.com/badge/spi-ca/logging)](http://goreportcard.com/report/spi-ca/logging)
|
[![Go Report](http://goreportcard.com/badge/spi-ca/logging](http://goreportcard.com/report/spi-ca/logging)
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
|
@ -21,4 +21,217 @@ go get amuz.es/src/go/logging
|
||||||
```
|
```
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
TBD
|
|
||||||
|
Create a `server.go` file with the following content:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/qiangxue/fasthttp-routing"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
router := routing.New()
|
||||||
|
|
||||||
|
router.Get("/", func(c *routing.Context) error {
|
||||||
|
fmt.Fprintf(c, "Hello, world!")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
panic(fasthttp.ListenAndServe(":8080", router.HandleRequest))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now run the following command to start the Web server:
|
||||||
|
|
||||||
|
```
|
||||||
|
go run server.go
|
||||||
|
```
|
||||||
|
|
||||||
|
You should be able to access URLs such as `http://localhost:8080`.
|
||||||
|
|
||||||
|
|
||||||
|
### Routes
|
||||||
|
|
||||||
|
ozzo-routing works by building a routing table in a router and then dispatching HTTP requests to the matching handlers
|
||||||
|
found in the routing table. An intuitive illustration of a routing table is as follows:
|
||||||
|
|
||||||
|
|
||||||
|
Routes | Handlers
|
||||||
|
--------------------|-----------------
|
||||||
|
`GET /users` | m1, m2, h1, ...
|
||||||
|
`POST /users` | m1, m2, h2, ...
|
||||||
|
`PUT /users/<id>` | m1, m2, h3, ...
|
||||||
|
`DELETE /users/<id>`| m1, m2, h4, ...
|
||||||
|
|
||||||
|
|
||||||
|
For an incoming request `GET /users`, the first route would match and the handlers m1, m2, and h1 would be executed.
|
||||||
|
If the request is `PUT /users/123`, the third route would match and the corresponding handlers would be executed.
|
||||||
|
Note that the token `<id>` can match any number of non-slash characters and the matching part can be accessed as
|
||||||
|
a path parameter value in the handlers.
|
||||||
|
|
||||||
|
**If an incoming request matches multiple routes in the table, the route added first to the table will take precedence.
|
||||||
|
All other matching routes will be ignored.**
|
||||||
|
|
||||||
|
The actual implementation of the routing table uses a variant of the radix tree data structure, which makes the routing
|
||||||
|
process as fast as working with a hash table, thanks to the inspiration from [httprouter](https://github.com/julienschmidt/httprouter).
|
||||||
|
|
||||||
|
To add a new route and its handlers to the routing table, call the `To` method like the following:
|
||||||
|
|
||||||
|
```go
|
||||||
|
router := routing.New()
|
||||||
|
router.To("GET", "/users", m1, m2, h1)
|
||||||
|
router.To("POST", "/users", m1, m2, h2)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use shortcut methods, such as `Get`, `Post`, `Put`, etc., which are named after the HTTP method names:
|
||||||
|
|
||||||
|
```go
|
||||||
|
router.Get("/users", m1, m2, h1)
|
||||||
|
router.Post("/users", m1, m2, h2)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have multiple routes with the same URL path but different HTTP methods, like the above example, you can
|
||||||
|
chain them together as follows,
|
||||||
|
|
||||||
|
```go
|
||||||
|
router.Get("/users", m1, m2, h1).Post(m1, m2, h2)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use the same set of handlers to handle the same URL path but different HTTP methods, you can take
|
||||||
|
the following shortcut:
|
||||||
|
|
||||||
|
```go
|
||||||
|
router.To("GET,POST", "/users", m1, m2, h)
|
||||||
|
```
|
||||||
|
|
||||||
|
A route may contain parameter tokens which are in the format of `<name:pattern>`, where `name` stands for the parameter
|
||||||
|
name, and `pattern` is a regular expression which the parameter value should match. A token `<name>` is equivalent
|
||||||
|
to `<name:[^/]*>`, i.e., it matches any number of non-slash characters. At the end of a route, an asterisk character
|
||||||
|
can be used to match any number of arbitrary characters. Below are some examples:
|
||||||
|
|
||||||
|
* `/users/<username>`: matches `/users/admin`
|
||||||
|
* `/users/accnt-<id:\d+>`: matches `/users/accnt-123`, but not `/users/accnt-admin`
|
||||||
|
* `/users/<username>/*`: matches `/users/admin/profile/address`
|
||||||
|
|
||||||
|
When a URL path matches a route, the matching parameters on the URL path can be accessed via `Context.Param()`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
router := routing.New()
|
||||||
|
|
||||||
|
router.Get("/users/<username>", func (c *routing.Context) error {
|
||||||
|
fmt.Fprintf(c, "Name: %v", c.Param("username"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Route Groups
|
||||||
|
|
||||||
|
Route group is a way of grouping together the routes which have the same route prefix. The routes in a group also
|
||||||
|
share the same handlers that are registered with the group via its `Use` method. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
router := routing.New()
|
||||||
|
api := router.Group("/api")
|
||||||
|
api.Use(m1, m2)
|
||||||
|
api.Get("/users", h1).Post(h2)
|
||||||
|
api.Put("/users/<id>", h3).Delete(h4)
|
||||||
|
```
|
||||||
|
|
||||||
|
The above `/api` route group establishes the following routing table:
|
||||||
|
|
||||||
|
|
||||||
|
Routes | Handlers
|
||||||
|
------------------------|-------------
|
||||||
|
`GET /api/users` | m1, m2, h1, ...
|
||||||
|
`POST /api/users` | m1, m2, h2, ...
|
||||||
|
`PUT /api/users/<id>` | m1, m2, h3, ...
|
||||||
|
`DELETE /api/users/<id>`| m1, m2, h4, ...
|
||||||
|
|
||||||
|
|
||||||
|
As you can see, all these routes have the same route prefix `/api` and the handlers `m1` and `m2`. In other similar
|
||||||
|
routing frameworks, the handlers registered with a route group are also called *middlewares*.
|
||||||
|
|
||||||
|
Route groups can be nested. That is, a route group can create a child group by calling the `Group()` method. The router
|
||||||
|
serves as the top level route group. A child group inherits the handlers registered with its parent group. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
router := routing.New()
|
||||||
|
router.Use(m1)
|
||||||
|
|
||||||
|
api := router.Group("/api")
|
||||||
|
api.Use(m2)
|
||||||
|
|
||||||
|
users := group.Group("/users")
|
||||||
|
users.Use(m3)
|
||||||
|
users.Put("/<id>", h1)
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the router serves as the parent of the `api` group which is the parent of the `users` group,
|
||||||
|
the `PUT /api/users/<id>` route is associated with the handlers `m1`, `m2`, `m3`, and `h1`.
|
||||||
|
|
||||||
|
|
||||||
|
### Router
|
||||||
|
|
||||||
|
Router manages the routing table and dispatches incoming requests to appropriate handlers. A router instance is created
|
||||||
|
by calling the `routing.New()` method.
|
||||||
|
|
||||||
|
To hook up router with fasthttp, use the following code:
|
||||||
|
|
||||||
|
```go
|
||||||
|
router := routing.New()
|
||||||
|
fasthttp.ListenAndServe(":8080", router.HandleRequest)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Handlers
|
||||||
|
|
||||||
|
A handler is a function with the signature `func(*routing.Context) error`. A handler is executed by the router if
|
||||||
|
the incoming request URL path matches the route that the handler is associated with. Through the `routing.Context`
|
||||||
|
parameter, you can access the request information in handlers.
|
||||||
|
|
||||||
|
A route may be associated with multiple handlers. These handlers will be executed in the order that they are registered
|
||||||
|
to the route. The execution sequence can be terminated in the middle using one of the following two methods:
|
||||||
|
|
||||||
|
* A handler returns an error: the router will skip the rest of the handlers and handle the returned error.
|
||||||
|
* A handler calls `Context.Abort()`: the router will simply skip the rest of the handlers. There is no error to be handled.
|
||||||
|
|
||||||
|
A handler can call `Context.Next()` to explicitly execute the rest of the unexecuted handlers and take actions after
|
||||||
|
they finish execution. For example, a response compression handler may start the output buffer, call `Context.Next()`,
|
||||||
|
and then compress and send the output to response.
|
||||||
|
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
For each incoming request, a `routing.Context` object is passed through the relevant handlers. Because `routing.Context`
|
||||||
|
embeds `fasthttp.RequestCtx`, you can access all properties and methods provided by the latter.
|
||||||
|
|
||||||
|
Additionally, the `Context.Param()` method allows handlers to access the URL path parameters that match the current route.
|
||||||
|
Using `Context.Get()` and `Context.Set()`, handlers can share data between each other. For example, an authentication
|
||||||
|
handler can store the authenticated user identity by calling `Context.Set()`, and other handlers can retrieve back
|
||||||
|
the identity information by calling `Context.Get()`.
|
||||||
|
|
||||||
|
Context also provides a handy `WriteData()` method that can be used to write data of arbitrary type to the response.
|
||||||
|
The `WriteData()` method can also be overridden (by replacement) to achieve more versatile response data writing.
|
||||||
|
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
A handler may return an error indicating some erroneous condition. Sometimes, a handler or the code it calls may cause
|
||||||
|
a panic. Both should be handled properly to ensure best user experience. It is recommended that you use
|
||||||
|
the `fault.Recover` handler or a similar error handler to handle these errors.
|
||||||
|
|
||||||
|
If an error is not handled by any handler, the router will handle it by calling its `handleError()` method which
|
||||||
|
simply sets an appropriate HTTP status code and writes the error message to the response.
|
||||||
|
|
||||||
|
When an incoming request has no matching route, the router will call the handlers registered via the `Router.NotFound()`
|
||||||
|
method. All the handlers registered via `Router.Use()` will also be called in advance. By default, the following two
|
||||||
|
handlers are registered with `Router.NotFound()`:
|
||||||
|
|
||||||
|
* `routing.MethodNotAllowedHandler`: a handler that sends an `Allow` HTTP header indicating the allowed HTTP methods for a requested URL
|
||||||
|
* `routing.NotFoundHandler`: a handler triggering 404 HTTP error
|
||||||
|
|
Loading…
Reference in New Issue