HISTORY.md: example of the new Dependency Injection features

Former-commit-id: 94294ffa96fafeb133b129f6f59c813d73ed05f1
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-04-30 22:50:49 +03:00
parent c3543528cf
commit f667bc5ff3
2 changed files with 216 additions and 40 deletions

View File

@ -164,15 +164,195 @@ Here is a preview of what the new Hero handlers look like:
### Request & Response & Path Parameters
![](https://github.com/kataras/explore/raw/master/iris-v12.2/hero/1.png)
**1.** Declare Go types for client's request body and a server's response.
```go
type (
request struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}
response struct {
ID uint64 `json:"id"`
Message string `json:"message"`
}
)
```
**2.** Create the route handler.
Path parameters and request body are binded automatically.
- **id uint64** binds to "id:uint64"
- **input request** binds to client request data such as JSON
```go
func updateUser(id uint64, input request) response {
return response{
ID: id,
Message: "User updated successfully",
}
}
```
**3.** Configure the container per group and register the route.
```go
app.Party("/user").ConfigureContainer(container)
func container(api *iris.APIContainer) {
api.Put("/{id:uint64}", updateUser)
}
```
**4.** Simulate a [client](https://curl.haxx.se/download.html) request which sends data to the server and displays the response.
```sh
curl --request PUT -d '{"firstanme":"John","lastname":"Doe"}' http://localhost:8080/user/42
```
```json
{
"id": 42,
"message": "User updated successfully"
}
```
### Custom Preflight
![](https://github.com/kataras/explore/raw/master/iris-v12.2/hero/2.png)
Before we continue to the next section, register dependencies, you may want to learn how a response can be customized through the `iris.Context` right before sent to the client.
### Custom Dependencies
The server will automatically execute the `Preflight(iris.Context) error` method of a function's output struct value right before send the response to the client.
![](https://github.com/kataras/explore/raw/master/iris-v12.2/hero/3.png)
Take for example that you want to fire different HTTP status codes depending on the custom logic inside your handler and also modify the value(response body) itself before sent to the client. Your response type should contain a `Preflight` method like below.
```go
type response struct {
ID uint64 `json:"id,omitempty"`
Message string `json:"message"`
Code int `json:"code"`
Timestamp int64 `json:"timestamp,omitempty"`
}
func (r *response) Preflight(ctx iris.Context) error {
if r.ID > 0 {
r.Timestamp = time.Now().Unix()
}
ctx.StatusCode(r.Code)
return nil
}
```
Now, each handler that returns a `*response` value will call the `response.Preflight` method automatically.
```go
func deleteUser(db *sql.DB, id uint64) *response {
// [...custom logic]
return &response{
Message: "User has been marked for deletion",
Code: iris.StatusAccepted,
}
}
```
If you register the route and fire a request you should see an output like this, the timestamp is filled and the HTTP status code of the response that the client will receive is 202 (Status Accepted).
```json
{
"message": "User has been marked for deletion",
"code": 202,
"timestamp": 1583313026
}
```
### Register Dependencies
**1.** Import packages to interact with a database.
The go-sqlite3 package is a database driver for [SQLite](https://www.sqlite.org/index.html).
```go
import "database/sql"
import _ "github.com/mattn/go-sqlite3"
```
**2.** Configure the container ([see above](#request--response--path-parameters)), register your dependencies. Handler expects an *sql.DB instance.
```go
localDB, _ := sql.Open("sqlite3", "./foo.db")
api.RegisterDependency(localDB)
```
**3.** Register a route to create a user.
```go
api.Post("/{id:uint64}", createUser)
```
**4.** The create user Handler.
The handler accepts a database and some client request data such as JSON, Protobuf, Form, URL Query and e.t.c. It Returns a response.
```go
func createUser(db *sql.DB, user request) *response {
// [custom logic using the db]
userID, err := db.CreateUser(user)
if err != nil {
return &response{
Message: err.Error(),
Code: iris.StatusInternalServerError,
}
}
return &response{
ID: userID,
Message: "User created",
Code: iris.StatusCreated,
}
}
```
**5.** Simulate a [client](https://curl.haxx.se/download.html) to create a user.
```sh
# JSON
curl --request POST -d '{"firstname":"John","lastname":"Doe"}' \
--header 'Content-Type: application/json' \
http://localhost:8080/user
```
```sh
# Form (multipart)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: multipart/form-data' \
--form 'firstname=John' \
--form 'lastname=Doe'
```
```sh
# Form (URL-encoded)
curl --request POST 'http://localhost:8080/users' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'firstname=John' \
--data-urlencode 'lastname=Doe'
```
```sh
# URL Query
curl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe'
```
Response:
```json
{
"id": 42,
"message": "User created",
"code": 201,
"timestamp": 1583313026
}
```
Other Improvements:
@ -184,11 +364,13 @@ Other Improvements:
#### DBUG Routes (1)
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug.png)
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug.png?v=0)
### DBUG Routes (2)
#### DBUG Routes (2)
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png)
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)
- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code.
- Server will not return neither log the `ErrServerClosed` if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`).

View File

@ -160,14 +160,8 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
}
}
// TODO: move this and make it easier to read when all cases are, visually, tested.
if logger := h.logger; logger != nil && logger.Level == golog.DebugLevel {
tr := "Routes"
if len(registeredRoutes) == 1 {
tr = tr[0 : len(tr)-1]
}
// logger.Debugf("%s: %d", tr, len(registeredRoutes))
// group routes by method and print them without the [DBUG] and time info,
// the route logs are colorful.
// Note: don't use map, we need to keep registered order, use
@ -182,42 +176,33 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
return
}
// bckpTimeFormat := logger.TimeFormat
// defer logger.SetTimeFormat(bckpTimeFormat)
// logger.SetTimeFormat("")
type methodCount struct {
type MethodRoutes struct {
method string
count int
routes []*Route
}
allMethods := append(AllMethods, MethodNone)
routeMethodCounts := make([]methodCount, 0, len(allMethods))
methodRoutes := make([]MethodRoutes, 0, len(allMethods))
for i, method := range allMethods {
methodRoutes := collect(method)
if len(methodRoutes) == 0 {
continue
}
routeMethodCounts = append(routeMethodCounts, methodCount{method, len(methodRoutes)})
for _, r := range methodRoutes {
r.Trace(logger.Printer)
}
if i != len(allMethods)-1 {
logger.Printer.Write(pio.NewLine)
for _, method := range allMethods {
routes := collect(method)
if len(routes) > 0 {
methodRoutes = append(methodRoutes, MethodRoutes{method, routes})
}
}
if n := len(routeMethodCounts); n > 0 {
if n := len(methodRoutes); n > 0 {
tr := "routes"
if len(registeredRoutes) == 1 {
tr = tr[0 : len(tr)-1]
}
fmt.Fprintf(logger.Printer, "%s API: %d registered %s (", golog.GetTextForLevel(golog.DebugLevel, true), len(registeredRoutes), tr)
for i, mc := range routeMethodCounts {
bckpNewLine := logger.NewLine
logger.NewLine = false
logger.Debugf("API: %d registered %s (", len(registeredRoutes), tr)
logger.NewLine = bckpNewLine
for i, m := range methodRoutes {
// @method: @count
if i > 0 {
if i == n-1 {
@ -226,13 +211,22 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
fmt.Fprint(logger.Printer, ", ")
}
}
fmt.Fprintf(logger.Printer, "%d ", mc.count)
pio.WriteRich(logger.Printer, mc.method, traceMethodColor(mc.method))
fmt.Fprintf(logger.Printer, "%d ", len(m.routes))
pio.WriteRich(logger.Printer, m.method, traceMethodColor(m.method))
}
fmt.Fprint(logger.Printer, ")\n")
}
for i, m := range methodRoutes {
for _, r := range m.routes {
r.Trace(logger.Printer)
}
if i != len(allMethods)-1 {
logger.Printer.Write(pio.NewLine)
}
}
}
return errgroup.Check(rp)