add content type and response text to the Controller 💯

Former-commit-id: 99cde0a445027b10839155501a7918732a783af3
This commit is contained in:
hiveminded 2017-09-02 14:32:14 +03:00
parent fe94840929
commit d7ec0d4416
6 changed files with 107 additions and 73 deletions

View File

@ -16,9 +16,9 @@ func main() {
app.Use(logger.New()) app.Use(logger.New())
// Method: GET // Method: GET
// Resource: http://localhost:8080/ // Resource: http://localhost:8080
app.Handle("GET", "/", func(ctx iris.Context) { app.Handle("GET", "/", func(ctx iris.Context) {
ctx.HTML("<b>Welcome!</b>") ctx.HTML("<h1>Welcome</h1>")
}) })
// same as app.Handle("GET", "/ping", [...]) // same as app.Handle("GET", "/ping", [...])
@ -31,7 +31,7 @@ func main() {
// Method: GET // Method: GET
// Resource: http://localhost:8080/hello // Resource: http://localhost:8080/hello
app.Get("/hello", func(ctx iris.Context) { app.Get("/hello", func(ctx iris.Context) {
ctx.JSON(iris.Map{"message": "Hello iris web framework."}) ctx.JSON(iris.Map{"message": "Hello Iris!"})
}) })
// http://localhost:8080 // http://localhost:8080

View File

@ -35,9 +35,7 @@ func main() {
app.Use(recover.New()) app.Use(recover.New())
app.Use(logger.New()) app.Use(logger.New())
app.Controller("/", new(IndexController)) app.Controller("/", new(ExampleController))
app.Controller("/ping", new(PingController))
app.Controller("/hello", new(HelloController))
// http://localhost:8080 // http://localhost:8080
// http://localhost:8080/ping // http://localhost:8080/ping
@ -45,63 +43,50 @@ func main() {
app.Run(iris.Addr(":8080")) app.Run(iris.Addr(":8080"))
} }
// IndexController serves the "/". // ExampleController serves the "/", "/ping" and "/hello".
type IndexController struct { type ExampleController struct {
// if you build with go1.8 you have to use the mvc package, `mvc.Controller` instead. // if you build with go1.8 you have to use the mvc package, `mvc.Controller` instead.
iris.Controller iris.Controller
} }
// Get serves // Get serves
// Method: GET // Method: GET
// Resource: http://localhost:8080/ // Resource: http://localhost:8080
func (c *IndexController) Get() { func (c *ExampleController) Get() {
c.Ctx.HTML("<b>Welcome!</b>") c.ContentType = "text/html"
c.Text = "<h1>Welcome!</h1>"
} }
// PingController serves the "/ping". // GetPing serves
type PingController struct {
iris.Controller
}
// Get serves
// Method: GET // Method: GET
// Resource: http://localhost:8080/ping // Resource: http://localhost:8080/ping
func (c *PingController) Get() { func (c *ExampleController) GetPing() {
c.Ctx.WriteString("pong") c.Text = "pong"
} }
// HelloController serves the "/hello". // GetHello serves
type HelloController struct {
iris.Controller
}
type myJSONData struct {
Message string `json:"message"`
}
// Get serves
// Method: GET // Method: GET
// Resource: http://localhost:8080/hello // Resource: http://localhost:8080/hello
func (c *HelloController) Get() { func (c *ExampleController) GetHello() {
c.Ctx.JSON(myJSONData{"Hello iris web framework."}) c.Ctx.JSON(iris.Map{"message": "Hello Iris!"})
} }
/* Can use more than one, the factory will make sure /* Can use more than one, the factory will make sure
that the correct http methods are being registered for each route that the correct http methods are being registered for each route
for this controller, uncomment these if you want: for this controller, uncomment these if you want:
func (c *HelloController) Post() {} func (c *ExampleController) Post() {}
func (c *HelloController) Put() {} func (c *ExampleController) Put() {}
func (c *HelloController) Delete() {} func (c *ExampleController) Delete() {}
func (c *HelloController) Connect() {} func (c *ExampleController) Connect() {}
func (c *HelloController) Head() {} func (c *ExampleController) Head() {}
func (c *HelloController) Patch() {} func (c *ExampleController) Patch() {}
func (c *HelloController) Options() {} func (c *ExampleController) Options() {}
func (c *HelloController) Trace() {} func (c *ExampleController) Trace() {}
*/ */
/* /*
func (c *HelloController) All() {} func (c *ExampleController) All() {}
// OR // OR
func (c *HelloController) Any() {} func (c *ExampleController) Any() {}
*/ */

View File

@ -1366,12 +1366,21 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader,
func (ctx *context) Redirect(urlToRedirect string, statusHeader ...int) { func (ctx *context) Redirect(urlToRedirect string, statusHeader ...int) {
ctx.StopExecution() ctx.StopExecution()
httpStatus := http.StatusFound // a 'temporary-redirect-like' which works better than for our purpose // get the previous status code given by the end-developer.
if len(statusHeader) > 0 && statusHeader[0] > 0 { status := ctx.GetStatusCode()
httpStatus = statusHeader[0] if len(statusHeader) > 0 {
// check if status code is passed via receivers.
if s := statusHeader[0]; s > 0 {
status = s
}
}
if status == 0 {
// if status remains zero then default it.
// a 'temporary-redirect-like' which works better than for our purpose
status = http.StatusFound
} }
http.Redirect(ctx.writer, ctx.request, urlToRedirect, httpStatus) http.Redirect(ctx.writer, ctx.request, urlToRedirect, status)
} }
// +------------------------------------------------------------+ // +------------------------------------------------------------+

View File

@ -169,7 +169,7 @@ func testTheRoutes(t *testing.T, tests []testRoute, debug bool) {
if method == "" { if method == "" {
method = tt.method method = tt.method
} }
ex := e.Request(tt.method, req.path) ex := e.Request(method, req.path)
if req.subdomain != "" { if req.subdomain != "" {
ex.WithURL("http://" + req.subdomain + ".localhost:8080") ex.WithURL("http://" + req.subdomain + ".localhost:8080")
} }

30
doc.go
View File

@ -163,10 +163,10 @@ Example code:
Listening and gracefully shutdown Listening and gracefully shutdown
You can listen to a server using any type of net.Listener or http.Server instance. You can start the server(s) listening to any type of `net.Listener` or even `http.Server` instance.
The method for initialization of the server should be passed at the end, via `Run` function. The method for initialization of the server should be passed at the end, via `Run` function.
Below you'll read some usage examples: Below you'll see some useful examples:
// Listening on tcp with network address 0.0.0.0:8080 // Listening on tcp with network address 0.0.0.0:8080
@ -190,13 +190,22 @@ Below you'll read some usage examples:
// Automatic TLS // Automatic TLS
app.Run(iris.AutoTLS("localhost:443")) app.Run(iris.AutoTLS(":443", "example.com", "admin@example.com"))
// UNIX socket // UNIX socket
l, err := netutil.UNIX("/tmpl/srv.sock", 0666) if errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) {
app.Logger().Fatal(errOs)
}
l, err := net.Listen("unix", socketFile)
if err != nil { if err != nil {
panic(err) app.Logger().Fatal(err)
}
if err = os.Chmod(socketFile, mode); err != nil {
app.Logger().Fatal(err)
} }
app.Run(iris.Listener(l)) app.Run(iris.Listener(l))
@ -454,7 +463,7 @@ Example code:
app.Any("/", handler) app.Any("/", handler)
func handler(ctx iris.Context){ func handler(ctx iris.Context){
ctx.Writef("Hello from method: %s and path: %s", ctx.Method(), ctx.Path()) ctx.Writef("Hello from method: %s and path: %s", ctx.Method(), ctx.Path())
} }
@ -471,15 +480,12 @@ A group can have a nested group too.
Example code: Example code:
users:= app.Party("/users", myAuthHandler) users := app.Party("/users", myAuthMiddlewareHandler)
// http://myhost.com/users/42/profile // http://myhost.com/users/42/profile
users.Get("/{userid:int}/profile", userProfileHandler) users.Get("/{id:int}/profile", userProfileHandler)
// http://myhost.com/users/messages/1 // http://myhost.com/users/messages/1
users.Get("/inbox/{messageid:int}", userMessageHandler) users.Get("/inbox/{id:int}", userMessageHandler)
app.Run(iris.Addr("myhost.com:80"))
Custom HTTP Errors Custom HTTP Errors

View File

@ -63,17 +63,18 @@ import (
// Look `core/router/APIBuilder#Controller` method too. // Look `core/router/APIBuilder#Controller` method too.
type Controller struct { type Controller struct {
// Name contains the current controller's full name. // Name contains the current controller's full name.
//
// doesn't change on different paths.
Name string Name string
// currentRoute is the current request context's route.
currentRoute context.RouteReadOnly
// contains the `Name` as different words, all lowercase, // contains the `Name` as different words, all lowercase,
// without the "Controller" suffix if exists. // without the "Controller" suffix if exists.
// we need this as field because the activator // we need this as field because the activator
// we will not try to parse these if not needed // we will not try to parse these if not needed
// it's up to the end-developer to call `RelPath()` or `RelTmpl()` // it's up to the end-developer to call `RelPath()` or `RelTmpl()`
// which will result to fill them. // which will result to fill them.
//
// doesn't change on different paths.
nameAsWords []string nameAsWords []string
// relPath the "as assume" relative request path. // relPath the "as assume" relative request path.
@ -81,10 +82,12 @@ type Controller struct {
// If UserController and request path is "/user/messages" then it's "/messages" // If UserController and request path is "/user/messages" then it's "/messages"
// if UserPostController and request path is "/user/post" then it's "/" // if UserPostController and request path is "/user/post" then it's "/"
// if UserProfile and request path is "/user/profile/likes" then it's "/likes" // if UserProfile and request path is "/user/profile/likes" then it's "/likes"
//
// doesn't change on different paths.
relPath string relPath string
// request path and its parameters, read-write. // request path and its parameters, read-write.
// Path is the current request path. // Path is the current request path, if changed then it redirects.
Path string Path string
// Params are the request path's parameters, i.e // Params are the request path's parameters, i.e
// for route like "/user/{id}" and request to "/user/42" // for route like "/user/{id}" and request to "/user/42"
@ -101,13 +104,19 @@ type Controller struct {
// If UserController then it's "user/" // If UserController then it's "user/"
// if UserPostController then it's "user/post/" // if UserPostController then it's "user/post/"
// if UserProfile then it's "user/profile/". // if UserProfile then it's "user/profile/".
//
// doesn't change on different paths.
relTmpl string relTmpl string
// view read and write, // view read and write,
// can be already set-ed by previous handlers as well. // can be already set-ed by previous handlers as well.
Layout string Layout string
Tmpl string Tmpl string
Data map[string]interface{} Data map[string]interface{}
ContentType string
Text string // or Text
// give access to the request context itself. // give access to the request context itself.
Ctx context.Context Ctx context.Context
} }
@ -127,10 +136,7 @@ func (c *Controller) getNameWords() []string {
// Route returns the current request controller's context read-only access route. // Route returns the current request controller's context read-only access route.
func (c *Controller) Route() context.RouteReadOnly { func (c *Controller) Route() context.RouteReadOnly {
if c.currentRoute == nil { return c.Ctx.GetCurrentRoute()
c.currentRoute = c.Ctx.GetCurrentRoute()
}
return c.currentRoute
} }
const slashStr = "/" const slashStr = "/"
@ -208,6 +214,13 @@ func (c *Controller) RelTmpl() string {
return c.relTmpl return c.relTmpl
} }
// Write writes to the client via the context's ResponseWriter.
// Controller completes the `io.Writer` interface for the shake of ease.
func (c *Controller) Write(contents []byte) (int, error) {
c.tryWriteHeaders()
return c.Ctx.ResponseWriter().Write(contents)
}
// BeginRequest starts the main controller // BeginRequest starts the main controller
// it initialize the Ctx and other fields. // it initialize the Ctx and other fields.
// //
@ -221,12 +234,26 @@ func (c *Controller) BeginRequest(ctx context.Context) {
c.Status = ctx.GetStatusCode() c.Status = ctx.GetStatusCode()
// share values // share values
c.Values = ctx.Values() c.Values = ctx.Values()
// view // view data for templates, remember
// each controller is a new instance, so
// checking for nil and then init those type of fields
// have no meaning.
c.Data = make(map[string]interface{}, 0) c.Data = make(map[string]interface{}, 0)
// context itself // context itself
c.Ctx = ctx c.Ctx = ctx
} }
func (c *Controller) tryWriteHeaders() {
if status := c.Status; status > 0 && status != c.Ctx.GetStatusCode() {
c.Ctx.StatusCode(status)
}
if contentType := c.ContentType; contentType != "" {
c.Ctx.ContentType(contentType)
}
}
// EndRequest is the final method which will be executed // EndRequest is the final method which will be executed
// before response sent. // before response sent.
// //
@ -236,25 +263,32 @@ func (c *Controller) BeginRequest(ctx context.Context) {
// It's called internally. // It's called internally.
// End-Developer can ovveride it but still should be called at the end. // End-Developer can ovveride it but still should be called at the end.
func (c *Controller) EndRequest(ctx context.Context) { func (c *Controller) EndRequest(ctx context.Context) {
if path := c.Path; path != "" && path != ctx.Path() { if ctx.ResponseWriter().Written() > 0 {
// then redirect
ctx.Redirect(path)
return return
} }
if status := c.Status; status > 0 && status != ctx.GetStatusCode() { if path := c.Path; path != "" && path != ctx.Path() {
ctx.StatusCode(status) // then redirect and exit.
ctx.Redirect(path, c.Status)
return
}
c.tryWriteHeaders()
if response := c.Text; response != "" {
ctx.WriteString(response)
return // exit here
} }
if view := c.Tmpl; view != "" { if view := c.Tmpl; view != "" {
if layout := c.Layout; layout != "" { if layout := c.Layout; layout != "" {
ctx.ViewLayout(layout) ctx.ViewLayout(layout)
} }
if data := c.Data; data != nil { if data := c.Data; len(data) > 0 {
for k, v := range data { for k, v := range data {
ctx.ViewData(k, v) ctx.ViewData(k, v)
} }
} }
ctx.View(view) ctx.View(view)
} }
} }