mirror of
https://github.com/labstack/echo.git
synced 2024-12-24 20:14:31 +02:00
Encapsulated fields and exposed public functions.
Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
parent
df924ffc11
commit
bf85c56b08
59
context.go
59
context.go
@ -11,9 +11,9 @@ type (
|
||||
// Context represents context for the current request. It holds request and
|
||||
// response objects, path parameters, data and registered handler.
|
||||
Context struct {
|
||||
Request *http.Request
|
||||
Response *Response
|
||||
Socket *websocket.Conn
|
||||
request *http.Request
|
||||
response *Response
|
||||
socket *websocket.Conn
|
||||
pnames []string
|
||||
pvalues []string
|
||||
store store
|
||||
@ -24,8 +24,8 @@ type (
|
||||
|
||||
func NewContext(req *http.Request, res *Response, e *Echo) *Context {
|
||||
return &Context{
|
||||
Request: req,
|
||||
Response: res,
|
||||
request: req,
|
||||
response: res,
|
||||
echo: e,
|
||||
pnames: make([]string, e.maxParam),
|
||||
pvalues: make([]string, e.maxParam),
|
||||
@ -33,6 +33,21 @@ func NewContext(req *http.Request, res *Response, e *Echo) *Context {
|
||||
}
|
||||
}
|
||||
|
||||
// Request returns *http.Request.
|
||||
func (c *Context) Request() *http.Request {
|
||||
return c.request
|
||||
}
|
||||
|
||||
// Response returns *Response.
|
||||
func (c *Context) Response() *Response {
|
||||
return c.response
|
||||
}
|
||||
|
||||
// Socket returns *websocket.Conn.
|
||||
func (c *Context) Socket() *websocket.Conn {
|
||||
return c.socket
|
||||
}
|
||||
|
||||
// P returns path parameter by index.
|
||||
func (c *Context) P(i uint8) (value string) {
|
||||
l := uint8(len(c.pnames))
|
||||
@ -57,7 +72,7 @@ func (c *Context) Param(name string) (value string) {
|
||||
// Bind binds the request body into specified type v. Default binder does it
|
||||
// based on Content-Type header.
|
||||
func (c *Context) Bind(i interface{}) error {
|
||||
return c.echo.binder(c.Request, i)
|
||||
return c.echo.binder(c.request, i)
|
||||
}
|
||||
|
||||
// Render invokes the registered HTML template renderer and sends a text/html
|
||||
@ -66,37 +81,37 @@ func (c *Context) Render(code int, name string, data interface{}) error {
|
||||
if c.echo.renderer == nil {
|
||||
return RendererNotRegistered
|
||||
}
|
||||
c.Response.Header().Set(ContentType, TextHTML+"; charset=utf-8")
|
||||
c.Response.WriteHeader(code)
|
||||
return c.echo.renderer.Render(c.Response, name, data)
|
||||
c.response.Header().Set(ContentType, TextHTML+"; charset=utf-8")
|
||||
c.response.WriteHeader(code)
|
||||
return c.echo.renderer.Render(c.response, name, data)
|
||||
}
|
||||
|
||||
// JSON sends an application/json response with status code.
|
||||
func (c *Context) JSON(code int, i interface{}) error {
|
||||
c.Response.Header().Set(ContentType, ApplicationJSON+"; charset=utf-8")
|
||||
c.Response.WriteHeader(code)
|
||||
return json.NewEncoder(c.Response).Encode(i)
|
||||
c.response.Header().Set(ContentType, ApplicationJSON+"; charset=utf-8")
|
||||
c.response.WriteHeader(code)
|
||||
return json.NewEncoder(c.response).Encode(i)
|
||||
}
|
||||
|
||||
// String sends a text/plain response with status code.
|
||||
func (c *Context) String(code int, s string) error {
|
||||
c.Response.Header().Set(ContentType, TextPlain+"; charset=utf-8")
|
||||
c.Response.WriteHeader(code)
|
||||
_, err := c.Response.Write([]byte(s))
|
||||
c.response.Header().Set(ContentType, TextPlain+"; charset=utf-8")
|
||||
c.response.WriteHeader(code)
|
||||
_, err := c.response.Write([]byte(s))
|
||||
return err
|
||||
}
|
||||
|
||||
// HTML sends a text/html response with status code.
|
||||
func (c *Context) HTML(code int, html string) error {
|
||||
c.Response.Header().Set(ContentType, TextHTML+"; charset=utf-8")
|
||||
c.Response.WriteHeader(code)
|
||||
_, err := c.Response.Write([]byte(html))
|
||||
c.response.Header().Set(ContentType, TextHTML+"; charset=utf-8")
|
||||
c.response.WriteHeader(code)
|
||||
_, err := c.response.Write([]byte(html))
|
||||
return err
|
||||
}
|
||||
|
||||
// NoContent sends a response with no body and a status code.
|
||||
func (c *Context) NoContent(code int) error {
|
||||
c.Response.WriteHeader(code)
|
||||
c.response.WriteHeader(code)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -117,11 +132,11 @@ func (c *Context) Set(key string, val interface{}) {
|
||||
|
||||
// Redirect redirects the request using http.Redirect with status code.
|
||||
func (c *Context) Redirect(code int, url string) {
|
||||
http.Redirect(c.Response, c.Request, url, code)
|
||||
http.Redirect(c.response, c.request, url, code)
|
||||
}
|
||||
|
||||
func (c *Context) reset(w http.ResponseWriter, r *http.Request, e *Echo) {
|
||||
c.Request = r
|
||||
c.Response.reset(w)
|
||||
c.request = r
|
||||
c.response.reset(w)
|
||||
c.echo = e
|
||||
}
|
||||
|
@ -91,26 +91,26 @@ func TestContext(t *testing.T) {
|
||||
|
||||
// JSON
|
||||
r.Header.Set(Accept, ApplicationJSON)
|
||||
c.Response.committed = false
|
||||
c.response.committed = false
|
||||
if he := c.JSON(http.StatusOK, u1); he != nil {
|
||||
t.Errorf("json %#v", he)
|
||||
}
|
||||
|
||||
// String
|
||||
r.Header.Set(Accept, TextPlain)
|
||||
c.Response.committed = false
|
||||
c.response.committed = false
|
||||
if he := c.String(http.StatusOK, "Hello, World!"); he != nil {
|
||||
t.Errorf("string %#v", he.Error)
|
||||
}
|
||||
|
||||
// HTML
|
||||
r.Header.Set(Accept, TextHTML)
|
||||
c.Response.committed = false
|
||||
c.response.committed = false
|
||||
if he := c.HTML(http.StatusOK, "Hello, <strong>World!</strong>"); he != nil {
|
||||
t.Errorf("html %v", he.Error)
|
||||
}
|
||||
|
||||
// Redirect
|
||||
c.Response.committed = false
|
||||
c.response.committed = false
|
||||
c.Redirect(http.StatusMovedPermanently, "http://labstack.github.io/echo")
|
||||
}
|
||||
|
58
echo.go
58
echo.go
@ -13,9 +13,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bradfitz/http2"
|
||||
"github.com/mattn/go-colorable"
|
||||
"golang.org/x/net/websocket"
|
||||
"github.com/bradfitz/http2"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -34,8 +34,8 @@ type (
|
||||
debug bool
|
||||
}
|
||||
HTTPError struct {
|
||||
Code int
|
||||
Message string
|
||||
code int
|
||||
message string
|
||||
}
|
||||
Middleware interface{}
|
||||
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
||||
@ -123,15 +123,21 @@ var (
|
||||
)
|
||||
|
||||
func NewHTTPError(code int, msg ...string) *HTTPError {
|
||||
he := &HTTPError{Code: code, Message: http.StatusText(code)}
|
||||
he := &HTTPError{code: code, message: http.StatusText(code)}
|
||||
for _, m := range msg {
|
||||
he.Message = m
|
||||
he.message = m
|
||||
}
|
||||
return he
|
||||
}
|
||||
|
||||
// Code returns code.
|
||||
func (e *HTTPError) Code() int {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// Error returns message.
|
||||
func (e *HTTPError) Error() string {
|
||||
return e.Message
|
||||
return e.message
|
||||
}
|
||||
|
||||
// New creates an Echo instance.
|
||||
@ -157,13 +163,13 @@ func New() (e *Echo) {
|
||||
code := http.StatusInternalServerError
|
||||
msg := http.StatusText(code)
|
||||
if he, ok := err.(*HTTPError); ok {
|
||||
code = he.Code
|
||||
msg = he.Message
|
||||
code = he.code
|
||||
msg = he.message
|
||||
}
|
||||
if e.Debug() {
|
||||
msg = err.Error()
|
||||
}
|
||||
http.Error(c.Response, msg, code)
|
||||
http.Error(c.response, msg, code)
|
||||
})
|
||||
e.SetBinder(func(r *http.Request, v interface{}) error {
|
||||
ct := r.Header.Get(ContentType)
|
||||
@ -283,12 +289,12 @@ func (e *Echo) WebSocket(path string, h HandlerFunc) {
|
||||
e.Get(path, func(c *Context) (err error) {
|
||||
wss := websocket.Server{
|
||||
Handler: func(ws *websocket.Conn) {
|
||||
c.Socket = ws
|
||||
c.Response.status = http.StatusSwitchingProtocols
|
||||
c.socket = ws
|
||||
c.response.status = http.StatusSwitchingProtocols
|
||||
err = h(c)
|
||||
},
|
||||
}
|
||||
wss.ServeHTTP(c.Response.writer, c.Request)
|
||||
wss.ServeHTTP(c.response.writer, c.request)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@ -313,7 +319,7 @@ func (e *Echo) Favicon(file string) {
|
||||
func (e *Echo) Static(path, root string) {
|
||||
fs := http.StripPrefix(path, http.FileServer(http.Dir(root)))
|
||||
e.Get(path+"*", func(c *Context) error {
|
||||
fs.ServeHTTP(c.Response, c.Request)
|
||||
fs.ServeHTTP(c.response, c.request)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -321,7 +327,7 @@ func (e *Echo) Static(path, root string) {
|
||||
// ServeFile serves a file.
|
||||
func (e *Echo) ServeFile(path, file string) {
|
||||
e.Get(path, func(c *Context) error {
|
||||
http.ServeFile(c.Response, c.Request, file)
|
||||
http.ServeFile(c.response, c.request, file)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -399,17 +405,17 @@ func (e *Echo) RunTLSServer(srv *http.Server, certFile, keyFile string) {
|
||||
e.run(srv, certFile, keyFile)
|
||||
}
|
||||
|
||||
func (e *Echo) run(s *http.Server, f ...string) {
|
||||
func (e *Echo) run(s *http.Server, files ...string) {
|
||||
s.Handler = e
|
||||
if e.http2 {
|
||||
http2.ConfigureServer(s, nil)
|
||||
}
|
||||
if len(f) == 0 {
|
||||
if len(files) == 0 {
|
||||
log.Fatal(s.ListenAndServe())
|
||||
} else if len(f) == 2 {
|
||||
log.Fatal(s.ListenAndServeTLS(f[0], f[1]))
|
||||
} else if len(files) == 2 {
|
||||
log.Fatal(s.ListenAndServeTLS(files[0], files[1]))
|
||||
} else {
|
||||
log.Fatal("echo: invalid TLS configuration")
|
||||
log.Fatal("echo => invalid TLS configuration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,10 +434,10 @@ func wrapMiddleware(m Middleware) MiddlewareFunc {
|
||||
return func(h HandlerFunc) HandlerFunc {
|
||||
return func(c *Context) (err error) {
|
||||
m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c.Response.writer = w
|
||||
c.Request = r
|
||||
c.response.writer = w
|
||||
c.request = r
|
||||
err = h(c)
|
||||
})).ServeHTTP(c.Response.writer, c.Request)
|
||||
})).ServeHTTP(c.response.writer, c.request)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -462,8 +468,8 @@ func wrapHandlerFuncMW(m HandlerFunc) MiddlewareFunc {
|
||||
func wrapHTTPHandlerFuncMW(m http.HandlerFunc) MiddlewareFunc {
|
||||
return func(h HandlerFunc) HandlerFunc {
|
||||
return func(c *Context) error {
|
||||
if !c.Response.committed {
|
||||
m.ServeHTTP(c.Response.writer, c.Request)
|
||||
if !c.response.committed {
|
||||
m.ServeHTTP(c.response.writer, c.request)
|
||||
}
|
||||
return h(c)
|
||||
}
|
||||
@ -479,12 +485,12 @@ func wrapHandler(h Handler) HandlerFunc {
|
||||
return h
|
||||
case http.Handler, http.HandlerFunc:
|
||||
return func(c *Context) error {
|
||||
h.(http.Handler).ServeHTTP(c.Response, c.Request)
|
||||
h.(http.Handler).ServeHTTP(c.response, c.request)
|
||||
return nil
|
||||
}
|
||||
case func(http.ResponseWriter, *http.Request):
|
||||
return func(c *Context) error {
|
||||
h(c.Response, c.Request)
|
||||
h(c.response, c.request)
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
|
@ -252,7 +252,7 @@ func TestEchoMethod(t *testing.T) {
|
||||
func TestWebSocket(t *testing.T) {
|
||||
e := New()
|
||||
e.WebSocket("/ws", func(c *Context) error {
|
||||
c.Socket.Write([]byte("test"))
|
||||
c.socket.Write([]byte("test"))
|
||||
return nil
|
||||
})
|
||||
srv := httptest.NewServer(e)
|
||||
|
@ -21,7 +21,7 @@ const (
|
||||
// For invalid credentials, it sends "401 - Unauthorized" response.
|
||||
func BasicAuth(fn AuthFunc) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
auth := c.Request.Header.Get(echo.Authorization)
|
||||
auth := c.Request().Header.Get(echo.Authorization)
|
||||
i := 0
|
||||
code := http.StatusBadRequest
|
||||
|
||||
|
@ -48,8 +48,8 @@ func TestBasicAuth(t *testing.T) {
|
||||
he := ba(c).(*echo.HTTPError)
|
||||
if ba(c) == nil {
|
||||
t.Error("expected `fail`, with incorrect password.")
|
||||
} else if he.Code != http.StatusUnauthorized {
|
||||
t.Errorf("expected status `401`, got %d", he.Code)
|
||||
} else if he.Code() != http.StatusUnauthorized {
|
||||
t.Errorf("expected status `401`, got %d", he.Code())
|
||||
}
|
||||
|
||||
// Empty Authorization header
|
||||
@ -58,8 +58,8 @@ func TestBasicAuth(t *testing.T) {
|
||||
he = ba(c).(*echo.HTTPError)
|
||||
if he == nil {
|
||||
t.Error("expected `fail`, with empty Authorization header.")
|
||||
} else if he.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status `400`, got %d", he.Code)
|
||||
} else if he.Code() != http.StatusBadRequest {
|
||||
t.Errorf("expected status `400`, got %d", he.Code())
|
||||
}
|
||||
|
||||
// Invalid Authorization header
|
||||
@ -69,8 +69,8 @@ func TestBasicAuth(t *testing.T) {
|
||||
he = ba(c).(*echo.HTTPError)
|
||||
if he == nil {
|
||||
t.Error("expected `fail`, with invalid Authorization header.")
|
||||
} else if he.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status `400`, got %d", he.Code)
|
||||
} else if he.Code() != http.StatusBadRequest {
|
||||
t.Errorf("expected status `400`, got %d", he.Code())
|
||||
}
|
||||
|
||||
// Invalid scheme
|
||||
@ -80,7 +80,7 @@ func TestBasicAuth(t *testing.T) {
|
||||
he = ba(c).(*echo.HTTPError)
|
||||
if he == nil {
|
||||
t.Error("expected `fail`, with invalid scheme.")
|
||||
} else if he.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status `400`, got %d", he.Code)
|
||||
} else if he.Code() != http.StatusBadRequest {
|
||||
t.Errorf("expected status `400`, got %d", he.Code())
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,18 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
gzipWriter struct {
|
||||
io.Writer
|
||||
*echo.Response
|
||||
http.ResponseWriter
|
||||
}
|
||||
)
|
||||
|
||||
func (g gzipWriter) Write(b []byte) (int, error) {
|
||||
return g.Writer.Write(b)
|
||||
func (w gzipWriter) Write(b []byte) (int, error) {
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
// Gzip returns a middleware which compresses HTTP response using gzip compression
|
||||
@ -26,12 +27,12 @@ func Gzip() echo.MiddlewareFunc {
|
||||
|
||||
return func(h echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
if strings.Contains(c.Request.Header.Get(echo.AcceptEncoding), scheme) {
|
||||
w := gzip.NewWriter(c.Response.Writer())
|
||||
if strings.Contains(c.Request().Header.Get(echo.AcceptEncoding), scheme) {
|
||||
w := gzip.NewWriter(c.Response().Writer())
|
||||
defer w.Close()
|
||||
gw := gzipWriter{Writer: w, Response: c.Response}
|
||||
c.Response.Header().Set(echo.ContentEncoding, scheme)
|
||||
c.Response = echo.NewResponse(gw)
|
||||
gw := gzipWriter{Writer: w, ResponseWriter: c.Response().Writer()}
|
||||
c.Response().Header().Set(echo.ContentEncoding, scheme)
|
||||
c.Response().SetWriter(gw)
|
||||
}
|
||||
return h(c)
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func TestGzip(t *testing.T) {
|
||||
// Content-Encoding header
|
||||
req.Header.Set(echo.AcceptEncoding, "gzip")
|
||||
w = httptest.NewRecorder()
|
||||
c.Response = echo.NewResponse(w)
|
||||
c.Response().SetWriter(w)
|
||||
Gzip()(h)(c)
|
||||
ce := w.Header().Get(echo.ContentEncoding)
|
||||
if ce != "gzip" {
|
||||
|
@ -16,14 +16,14 @@ func Logger() echo.MiddlewareFunc {
|
||||
c.Error(err)
|
||||
}
|
||||
end := time.Now()
|
||||
method := c.Request.Method
|
||||
path := c.Request.URL.Path
|
||||
method := c.Request().Method
|
||||
path := c.Request().URL.Path
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
size := c.Response.Size()
|
||||
size := c.Response().Size()
|
||||
|
||||
n := c.Response.Status()
|
||||
n := c.Response().Status()
|
||||
code := color.Green(n)
|
||||
switch {
|
||||
case n >= 500:
|
||||
|
@ -21,14 +21,14 @@ func TestLogger(t *testing.T) {
|
||||
Logger()(h)(c)
|
||||
|
||||
// Status 4xx
|
||||
c.Response = echo.NewResponse(w)
|
||||
c = echo.NewContext(req, echo.NewResponse(w), e)
|
||||
h = func(c *echo.Context) error {
|
||||
return c.String(http.StatusNotFound, "test")
|
||||
}
|
||||
Logger()(h)(c)
|
||||
|
||||
// Status 5xx
|
||||
c.Response = echo.NewResponse(w)
|
||||
c = echo.NewContext(req, echo.NewResponse(w), e)
|
||||
h = func(c *echo.Context) error {
|
||||
return c.String(http.StatusInternalServerError, "test")
|
||||
}
|
||||
|
@ -15,10 +15,10 @@ type (
|
||||
// path.
|
||||
func StripTrailingSlash() echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
p := c.Request.URL.Path
|
||||
p := c.Request().URL.Path
|
||||
l := len(p)
|
||||
if p[l-1] == '/' {
|
||||
c.Request.URL.Path = p[:l-1]
|
||||
c.Request().URL.Path = p[:l-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -36,7 +36,7 @@ func RedirectToSlash(opts ...RedirectToSlashOptions) echo.HandlerFunc {
|
||||
}
|
||||
|
||||
return func(c *echo.Context) error {
|
||||
p := c.Request.URL.Path
|
||||
p := c.Request().URL.Path
|
||||
l := len(p)
|
||||
if p[l-1] != '/' {
|
||||
c.Redirect(code, p+"/")
|
||||
|
@ -13,7 +13,7 @@ func TestStripTrailingSlash(t *testing.T) {
|
||||
res := echo.NewResponse(httptest.NewRecorder())
|
||||
c := echo.NewContext(req, res, echo.New())
|
||||
StripTrailingSlash()(c)
|
||||
p := c.Request.URL.Path
|
||||
p := c.Request().URL.Path
|
||||
if p != "/users" {
|
||||
t.Errorf("expected path `/users` got, %s.", p)
|
||||
}
|
||||
@ -31,7 +31,7 @@ func TestRedirectToSlash(t *testing.T) {
|
||||
}
|
||||
|
||||
// Location header
|
||||
l := c.Response.Header().Get("Location")
|
||||
l := c.Response().Header().Get("Location")
|
||||
if l != "/users/" {
|
||||
t.Errorf("expected Location header `/users/`, got %s.", l)
|
||||
}
|
||||
|
@ -26,6 +26,10 @@ func (r *Response) Header() http.Header {
|
||||
return r.writer.Header()
|
||||
}
|
||||
|
||||
func (r *Response) SetWriter(w http.ResponseWriter) {
|
||||
r.writer = w
|
||||
}
|
||||
|
||||
func (r *Response) Writer() http.ResponseWriter {
|
||||
return r.writer
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user