1
0
mirror of https://github.com/labstack/echo.git synced 2026-05-16 09:48:24 +02:00

updated migration guide

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana
2016-11-13 09:36:57 -08:00
parent 2f70d3e1c7
commit c5a3575d4c
17 changed files with 147 additions and 237 deletions
+1 -1
View File
@@ -21,7 +21,7 @@
## Performance ## Performance
![Performance](http://i.imgur.com/F2V7TfO.png) ![Performance](https://i.imgur.com/F2V7TfO.png)
## Quick Start ## Quick Start
+22 -18
View File
@@ -13,8 +13,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"golang.org/x/net/websocket"
) )
type ( type (
@@ -175,16 +173,15 @@ type (
} }
context struct { context struct {
request *http.Request request *http.Request
response *Response response *Response
webSocket *websocket.Conn path string
path string pnames []string
pnames []string pvalues []string
pvalues []string query url.Values
query url.Values handler HandlerFunc
handler HandlerFunc store Map
store Map echo *Echo
echo *Echo
} }
) )
@@ -238,15 +235,22 @@ func (c *context) SetPath(p string) {
c.path = p c.path = p
} }
func (c *context) Param(name string) (value string) { func (c *context) Param(name string) string {
l := len(c.pnames)
for i, n := range c.pnames { for i, n := range c.pnames {
if n == name && i < l { if i < len(c.pnames) {
value = c.pvalues[i] if strings.HasPrefix(n, name) {
break return c.pvalues[i]
}
// Param name with aliases
for _, p := range strings.Split(n, ",") {
if p == name {
return c.pvalues[i]
}
}
} }
} }
return return ""
} }
func (c *context) ParamNames() []string { func (c *context) ParamNames() []string {
+12
View File
@@ -250,6 +250,18 @@ func TestContextPathParam(t *testing.T) {
assert.Equal(t, "501", c.Param("fid")) assert.Equal(t, "501", c.Param("fid"))
} }
func TestContextPathParamNamesAlais(t *testing.T) {
e := New()
req, _ := http.NewRequest(GET, "/", nil)
c := e.NewContext(req, nil)
c.SetParamNames("id,name")
c.SetParamValues("joe")
assert.Equal(t, "joe", c.Param("id"))
assert.Equal(t, "joe", c.Param("name"))
}
func TestContextFormValue(t *testing.T) { func TestContextFormValue(t *testing.T) {
f := make(url.Values) f := make(url.Values)
f.Set("name", "Jon Snow") f.Set("name", "Jon Snow")
+8 -1
View File
@@ -1,5 +1,7 @@
package echo package echo
import "strings"
type ( type (
// Router is the registry of all registered routes for an `Echo` instance for // Router is the registry of all registered routes for an `Echo` instance for
// request matching and URL path parameter parsing. // request matching and URL path parameter parsing.
@@ -170,7 +172,12 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
if h != nil { if h != nil {
cn.addHandler(method, h) cn.addHandler(method, h)
cn.ppath = ppath cn.ppath = ppath
cn.pnames = pnames for i, n := range cn.pnames {
// Param name aliases
if !strings.Contains(n, pnames[i]) {
cn.pnames[i] += "," + pnames[i]
}
}
} }
} }
return return
+6 -3
View File
@@ -3,6 +3,7 @@ package echo
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -823,9 +824,11 @@ func TestRouterAPI(t *testing.T) {
c := e.NewContext(nil, nil).(*context) c := e.NewContext(nil, nil).(*context)
for _, route := range gitHubAPI { for _, route := range gitHubAPI {
r.Find(route.Method, route.Path, c) r.Find(route.Method, route.Path, c)
for i, n := range c.pnames { for _, n := range c.pnames {
if assert.NotEmpty(t, n) { for _, p := range strings.Split(n, ",") {
assert.Equal(t, n, c.pnames[i]) if assert.NotEmpty(t, p) {
assert.Equal(t, c.Param(p), ":"+p)
}
} }
} }
} }
+1 -1
View File
@@ -1,2 +1,2 @@
build: build:
rm -rf public && hugo rm -rf public/v3 && hugo
+2 -1
View File
@@ -1,9 +1,10 @@
{ {
"baseurl": "https://echo.labstack.com", "baseurl": "https://echo.labstack.com/",
"languageCode": "en-us", "languageCode": "en-us",
"title": "Echo - Fast and unfancy HTTP server framework for Go (Golang)", "title": "Echo - Fast and unfancy HTTP server framework for Go (Golang)",
"canonifyurls": true, "canonifyurls": true,
"googleAnalytics": "UA-85059636-2", "googleAnalytics": "UA-85059636-2",
"publishdir": "public/v3",
"permalinks": { "permalinks": {
"guide": "/guide/:filename", "guide": "/guide/:filename",
"middleware": "/middleware/:filename", "middleware": "/middleware/:filename",
+15 -64
View File
@@ -11,8 +11,6 @@ description = "Customizing Echo"
### HTTP Error Handler ### HTTP Error Handler
`Echo#SetHTTPErrorHandler(h HTTPErrorHandler)` registers a custom `Echo#HTTPErrorHandler`.
Default HTTP error handler rules: Default HTTP error handler rules:
- If error is of type `Echo#HTTPError` it sends HTTP response with status code `HTTPError.Code` - If error is of type `Echo#HTTPError` it sends HTTP response with status code `HTTPError.Code`
@@ -20,79 +18,32 @@ and message `HTTPError.Message`.
- Else it sends `500 - Internal Server Error`. - Else it sends `500 - Internal Server Error`.
- If debug mode is enabled, it uses `error.Error()` as status message. - If debug mode is enabled, it uses `error.Error()` as status message.
### Debug You can also set a custom HTTP error handler using `Echo#HTTPErrorHandler`.
`Echo#SetDebug(on bool)` enable/disable debug mode. ### Debugging
`Echo#Debug` enables/disables debug mode.
### Logging ### Logging
#### Custom Logger
`Echo#SetLogger(l log.Logger)`
SetLogger defines a custom logger.
#### Log Output #### Log Output
`Echo#SetLogOutput(w io.Writer)` sets the output destination for the logger. Default `Echo#Logger.SetOutput(io.Writer)` sets the output destination for the logger.
value `os.Stdout` Default value `os.Stdout`
To completely disable logs use `Echo#SetLogOutput(io.Discard)` To completely disable logs use `Echo#Logger.SetOutput(io.Discard)` or `Echo#Logger.SetLevel(log.OFF)`
#### Log Level #### Log Level
`Echo#SetLogLevel(l log.Level)` `Echo#Logger.SetLevel(log.Lvl)`
SetLogLevel sets the log level for the logger. Default value `5` (OFF). SetLogLevel sets the log level for the logger. Default value `OFF`.
Possible values: Possible values:
- `0` (DEBUG) - `DEBUG`
- `1` (INFO) - `INFO`
- `2` (WARN) - `WARN`
- `3` (ERROR) - `ERROR`
- `4` (FATAL) - `OFF`
- `5` (OFF)
### HTTP Engine You can also set a custom logger using `Echo#Logger`.
Echo currently supports standard and [fasthttp](https://github.com/valyala/fasthttp)
server engines. Echo utilizes interfaces to abstract the internal implementation
of these servers so you can seamlessly switch from one engine to another based on
your preference.
#### Running a standard HTTP server
`e.Run(standard.New(":1323"))`
#### Running a fasthttp server
`e.Run(fasthttp.New(":1323"))`
#### Running a server with TLS configuration
`e.Run(<engine>.WithTLS(":1323", "<certFile>", "<keyFile>"))`
#### Running a server with engine configuration
`e.Run(<engine>.WithConfig(<config>))`
##### Configuration
```go
Config struct {
Address string // TCP address to listen on.
Listener net.Listener // Custom `net.Listener`. If set, server accepts connections on it.
TLSCertFile string // TLS certificate file path.
TLSKeyFile string // TLS key file path.
ReadTimeout time.Duration // Maximum duration before timing out read of the request.
WriteTimeout time.Duration // Maximum duration before timing out write of the response.
}
```
#### Access internal server instance and configure its properties
```go
s := standard.New(":1323")
s.MaxHeaderBytes = 1 << 20
e.Run(s)
```
+13 -48
View File
@@ -9,82 +9,47 @@ description = "Frequently asked questions in Echo"
## FAQ ## FAQ
Q: **How to retrieve `*http.Request` and `http.ResponseWriter` from `echo.Context`?** Q: How to retrieve `*http.Request` and `http.ResponseWriter` from `echo.Context`?
- `http.Request` > `c.Request().(*standard.Request).Request` - `http.Request` > `c.Request()`
- `http.ResponseWriter` > `c.Response()` - `http.ResponseWriter` > `c.Response()`
> Standard engine only Q: How to use standard handler `func(http.ResponseWriter, *http.Request)` with Echo?
Q: **How to use standard handler `func(http.ResponseWriter, *http.Request)` with Echo?**
```go ```go
func handler(w http.ResponseWriter, r *http.Request) { func handler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Handler!") io.WriteString(w, "Echo!")
} }
func main() { func main() {
e := echo.New() e := echo.New()
e.GET("/", standard.WrapHandler(http.HandlerFunc(handler))) e.GET("/", echo.WrapHandler(http.HandlerFunc(handler)))
e.Run(standard.New(":1323")) e.Start(":1323")
} }
``` ```
Q: **How to use fasthttp handler `func(fasthttp.RequestCtx)` with Echo?** Q: How to use standard middleware `func(http.Handler) http.Handler` with Echo?
```go
func handler(c *fh.RequestCtx) {
io.WriteString(c, "Handler!")
}
func main() {
e := echo.New()
e.GET("/", fasthttp.WrapHandler(handler))
e.Run(fasthttp.New(":1323"))
}
```
Q: **How to use standard middleware `func(http.Handler) http.Handler` with Echo?**
```go ```go
func middleware(h http.Handler) http.Handler { func middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
println("Middleware!") println("middleware")
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
}) })
} }
func main() { func main() {
e := echo.New() e := echo.New()
e.Use(standard.WrapMiddleware(middleware)) e.Use(echo.WrapMiddleware(middleware))
e.GET("/", func(c echo.Context) error { e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "OK") return c.String(http.StatusOK, "Echo!")
}) })
e.Run(standard.New(":1323")) e.Start(":1323")
} }
``` ```
Q: **How to use fasthttp middleware `func(http.Handler) http.Handler` with Echo?** Q: How to run Echo on a specific IP address?
```go ```go
func middleware(h fh.RequestHandler) fh.RequestHandler { e.Start("<ip>:<port>")
return func(ctx *fh.RequestCtx) {
println("Middleware!")
h(ctx)
}
}
func main() {
e := echo.New()
e.Use(fasthttp.WrapMiddleware(middleware))
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "OK")
})
e.Run(fasthttp.New(":1323"))
}
``` ```
<!-- ### Q: How to run Echo on specific IP and port?
```go
``` -->
+2 -2
View File
@@ -15,9 +15,9 @@ Echo is developed and tested using Go `1.6.x` and `1.7.x`
$ go get -u github.com/labstack/echo $ go get -u github.com/labstack/echo
``` ```
> Ideally, you should rely on a [package manager](https://github.com/avelino/awesome-go#package-management) like glide or govendor to use a specific [version](https://github.com/labstack/echo/releases) of Echo. > Ideally you should rely on a [package manager](https://github.com/avelino/awesome-go#package-management) like glide or govendor to use a specific [version](https://github.com/labstack/echo/releases) of Echo.
### [Migrating from v1](/guide/migrating) ### [Migrating Guide](/guide/migration)
Echo follows [semantic versioning](http://semver.org) managed through GitHub releases. Echo follows [semantic versioning](http://semver.org) managed through GitHub releases.
Specific version of Echo can be installed using a [package manager](https://github.com/avelino/awesome-go#package-management). Specific version of Echo can be installed using a [package manager](https://github.com/avelino/awesome-go#package-management).
-93
View File
@@ -1,93 +0,0 @@
+++
title = "Migrating"
description = "Migrating from Echo v1 to v2"
[menu.side]
name = "Migrating"
parent = "guide"
weight = 2
+++
## Migrating from v1
### Change Log
- Good news, 85% of the API remains the same.
- `Engine` interface to abstract `HTTP` server implementation, allowing
us to use HTTP servers beyond Go standard library. It currently supports standard and [fasthttp](https://github.com/valyala/fasthttp) server.
- Context, Request and Response are converted to interfaces. [More...](https://github.com/labstack/echo/issues/146)
- Handler signature is changed to `func (c echo.Context) error`.
- Dropped auto wrapping of handler and middleware to enforce compile time check.
- APIs to run middleware before or after the router, which doesn't require `Echo#Hook` API now.
- Ability to define middleware at route level.
- `Echo#HTTPError` exposed it's fields `Code` and `Message`.
- Option to specify log format in logger middleware and default logger.
#### API
v1 | v2
--- | ---
`Context#Query()` | `Context#QueryParam()`
`Context#Form()` | `Context#FormValue()`
### FAQ
Q. How to access original objects from interfaces?
A. Only if you need to...
```go
// `*http.Request`
c.Request().(*standard.Request).Request
// `*http.URL`
c.Request().URL().(*standard.URL).URL
// Request `http.Header`
c.Request().Header().(*standard.Header).Header
// `http.ResponseWriter`
c.Response().(*standard.Response).ResponseWriter
// Response `http.Header`
c.Response().Header().(*standard.Header).Header
```
Q. How to use standard handler and middleware?
A.
```go
package main
import (
"net/http"
"github.com/labstack/echo"
"github.com/labstack/echo/engine/standard"
)
// Standard middleware
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
println("standard middleware")
next.ServeHTTP(w, r)
})
}
// Standard handler
func handler(w http.ResponseWriter, r *http.Request) {
println("standard handler")
}
func main() {
e := echo.New()
e.Use(standard.WrapMiddleware(middleware))
e.GET("/", standard.WrapHandler(http.HandlerFunc(handler)))
e.Run(standard.New(":1323"))
}
```
### Next?
- Browse through [recipes](/recipes/hello-world) freshly converted to v2.
- Read documentation and dig into test cases.
+50
View File
@@ -0,0 +1,50 @@
+++
title = "Migration"
description = "Migration"
[menu.side]
name = "Migration"
parent = "guide"
weight = 2
+++
## V3
### Change Log
- Automatic TLS certificates via [Let's Encrypt](https://letsencrypt.org/)
- Built-in support for graceful shutdown
- Dropped static middleware in favor of `Echo#Static`
- Utility functions to wrap standard handler and middleware
- `Map` type as shorthand for `map[string]interface{}`
- Context now wraps standard net/http Request and Response
- New configuration
- `Echo#ShutdownTimeout`
- `Echo#DisableHTTP2`
- New API
- `Echo#Start()`
- `Echo#StartTLS()`
- `Echo#StartAutoTLS()`
- `Echo#StartServer()`
- `Echo#Shutdown()`
- `Echo#ShutdownTLS()`
- `Context#Scheme()`
- `Context#RealIP()`
- `Context#IsTLS()`
- Exposed the following properties instead of setter / getter functions on `Echo` instance:
- `Binder`
- `Renderer`
- `HTTPErrorHandler`
- `Debug`
- `Logger`
- Enhanced redirect and CORS middleware
- Dropped API
- `Echo#Run()`
- `Context#P()`
- Dropped standard `Context` support
- Dropped support for `fasthttp`
- Dropped deprecated API
- Moved `Logger` interface to root level
- Moved website and recipes to the main repo
- Updated docs and fixed numerous issues
### [Recipes](/recipes/hello-world)
+3 -3
View File
@@ -8,7 +8,6 @@ title = "Index"
- Optimized HTTP router which smartly prioritize routes - Optimized HTTP router which smartly prioritize routes
- Build robust and scalable RESTful APIs - Build robust and scalable RESTful APIs
- Run with standard HTTP server or FastHTTP server
- Group APIs - Group APIs
- Extensible middleware framework - Extensible middleware framework
- Define middleware at root, group or route level - Define middleware at root, group or route level
@@ -18,11 +17,12 @@ title = "Index"
- Template rendering with any template engine - Template rendering with any template engine
- Define your format for the logger - Define your format for the logger
- Highly customizable - Highly customizable
- Automatic TLS via Let’s Encrypt
- Built-in graceful shutdown
## Performance ## Performance
<img style="width: 75%;" src="http://i.imgur.com/F2V7TfO.png" alt="Performance"> <img style="width: 75%;" src="https://i.imgur.com/F2V7TfO.png" alt="Performance">
## Quick Start ## Quick Start
+1
View File
@@ -40,3 +40,4 @@
<script async defer id="github-bjs" src="//buttons.github.io/buttons.js"></script> <script async defer id="github-bjs" src="//buttons.github.io/buttons.js"></script>
<script src="https://cdn.labstack.com/scripts/prism.js"></script> <script src="https://cdn.labstack.com/scripts/prism.js"></script>
<script src="https://cdn.labstack.com/scripts/doc.js"></script> <script src="https://cdn.labstack.com/scripts/doc.js"></script>
<script src="{{ .Site.BaseURL }}scripts/main.js"></script>
+1 -1
View File
@@ -24,7 +24,7 @@
<link rel="stylesheet" href="https://cdn.labstack.com/styles/prism.css"> <link rel="stylesheet" href="https://cdn.labstack.com/styles/prism.css">
<link rel="stylesheet" href="https://cdn.labstack.com/styles/base.css"> <link rel="stylesheet" href="https://cdn.labstack.com/styles/base.css">
<link rel="stylesheet" href="https://cdn.labstack.com/styles/doc.css"> <link rel="stylesheet" href="https://cdn.labstack.com/styles/doc.css">
<link rel="stylesheet" href="{{ .Site.BaseURL }}/styles/main.css"> <link rel="stylesheet" href="{{ .Site.BaseURL}}/styles/main.css">
<script> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+7 -1
View File
@@ -5,9 +5,15 @@
<a class="support w3-btn w3-white w3-border w3-border-theme w3-round-xlarge" href="/support-echo"> <a class="support w3-btn w3-white w3-border w3-border-theme w3-round-xlarge" href="/support-echo">
<i class="fa fa-heart" aria-hidden="true"></i> Support Echo <i class="fa fa-heart" aria-hidden="true"></i> Support Echo
</a> </a>
<h4>
<select class="w3-select w3-border" onchange="version(this);">
<option value="/v2">v2</option>
<option value="/" selected>v3</option>
</select>
</h4>
{{ $currentNode := . }} {{ $currentNode := . }}
{{ range .Site.Menus.side }} {{ range .Site.Menus.side }}
<h4>{{ .Pre }} {{ .Name }}</h4> <h3>{{ .Pre }} {{ .Name }}</h3>
{{ range .Children }} {{ range .Children }}
<a{{ if $currentNode.IsMenuCurrent "side" . }} class="active"{{ end }} href="{{ .URL }}"> <a{{ if $currentNode.IsMenuCurrent "side" . }} class="active"{{ end }} href="{{ .URL }}">
{{ .Name }} {{ .Name }}
+3
View File
@@ -0,0 +1,3 @@
function version(e) {
window.location = e.value;
}