gowww/app is a full featured HTTP framework for any web app.
It greatly increases productivity by providing helpers at all levels while maintaining best performance.
-
Install gowww/app:
go get github.com/gowww/app
-
Import it in your new app:
import github.com/gowww/app
There are methods for common HTTP methods:
app.Get("/", func(c *app.Context) {
// Write response for GET /
})
app.Post("/", func(c *app.Context) {
// Write response for POST /
})
app.Put("/", func(c *app.Context) {
// Write response for PUT /
})
app.Patch("/", func(c *app.Context) {
// Write response for PATCH /
})
app.Delete("/", func(c *app.Context) {
// Write response for DELETE /
})
A named parameter begins with :
and matches any value until the next /
in path.
To retrieve the value, ask Context.PathValue
.
It will return the value as a string (empty if the parameter doesn't exist).
Example, with a parameter id
:
app.Get("/users/:id", func(c *app.Context) {
id := c.PathValue("id")
fmt.Fprintf(w, "Page of user #%s", id)
}))
If a parameter must match an exact pattern (digits only, for example), you can also set a regular expression constraint just after the parameter name and another :
:
app.Get(`/users/:id:^\d+$`, func(c *app.Context) {
id := c.PathValue("id")
fmt.Fprintf(w, "Page of user #%s", id)
}))
If you don't need to retrieve the parameter value but only use a regular expression, you can omit the parameter name.
A trailing slash behaves like a wildcard by matching the beginning of the request path and keeping the rest as a parameter value, under *
:
rt.Get("/files/", func(c *app.Context) {
filepath := c.PathValue("*")
fmt.Fprintf(w, "Get file %s", filepath)
}))
For more details, see gowww/router.
A routing group works like the top-level router but prefixes all subroute paths:
api := app.Group("/api")
{
v1 := app.Group("/v1")
{
v1.Get("/user", func(c *app.Context) { // Write response for GET /api/v1/user })
v1.Get("/item", func(c *app.Context) { // Write response for GET /api/v1/item })
}
v2 := app.Group("/v2")
{
v2.Get("/user", func(c *app.Context) { // Write response for GET /api/v2/user })
v2.Get("/item", func(c *app.Context) { // Write response for GET /api/v2/item })
}
}
You can set a custom "not found" handler with NotFound
:
app.NotFound(func(c *app.Context) {
c.Status(http.StatusNotFound)
c.View("notFound")
})
The app is also recovered from panics so you can set a custom "serving error" handler (which is used only when the response is not already written) with Error
and retrive the recovered error value with Context.Error
:
app.Error(func(c *app.Context) {
c.Status(http.StatusInternalServerError)
if c.Error() == "cannot open file" {
c.View("errorStorage")
return
}
c.View("error")
})
A Context
is always used inside a Handler
.
It contains the original request and response writer but provides all the necessary helpers to access them:
Use Context.Req
to access the original request:
app.Get("/", func(c *app.Context) {
r := c.Req
})
Use Context.FormValue
to access a value from URL or body.
You can also use Context.HasFormValue
to check its existence:
app.Get("/", func(c *app.Context) {
if c.HasFormValue("id") {
id := c.FormValue("id")
}
})
Use Context.Res
to access the original response writer:
app.Get("/", func(c *app.Context) {
w := c.Res
})
Use Context.Text
or Context.Bytes
to send a string:
app.Get("/", func(c *app.Context) {
c.Text("Hello")
c.Bytes([]byte("World"))
})
Use Context.JSON
to send a JSON formatted response:
app.Get(`/users/:id:^\d+$/files/`, func(c *app.Context) {
c.JSON(map[string]interface{}{
"userID": c.PathValue("id"),
"filepath": c.PathValue("*"),
})
})
Use Context.Status
to set the response status code:
app.Get("/", func(c *app.Context) {
c.Status(http.StatusCreated)
})
Use Context.NotFound
to send a "not found" response:
app.Get("/", func(c *app.Context) {
c.NotFound()
})
Use Context.Panic
to log an error and send a "serving error" response:
app.Get("/", func(c *app.Context) {
c.Panic("database connection failed")
})
Use Context.Redirect
to redirect the client:
app.Get("/old", func(c *app.Context) {
c.Redirect("/new", http.StatusMovedPermanently)
})
Use Context.Push
to initiate an HTTP/2 server push:
app.Get("/", func(c *app.Context) {
c.Push("/static/main.css", nil)
})
You can use context values kept inside the context for future usage downstream (like views or subhandlers).
Use Context.Set
to set a value:
app.Get("/", func(c *app.Context) {
c.Set("clientCountry", "UK")
})
Use Context.Get
to retrieve a value:
app.Get("/", func(c *app.Context) {
clientCountry := c.Get("clientCountry")
})
To have translations accessible all over your app, use Localize
with your locales, their translations (a map of string to string) and the default locale:
var locales = app.Locales{
language.English: {
"hello": "Hello!",
},
language.French: {
"hello": "Bonjour !",
},
}
app.Localize(locales, language.English)
In your views, use function t
(or its variants: tn
, thtml
, tnhtml
) to get a translation:
<h1>{{t .c "hello"}}</h1>
For more details, see gowww/i18n.
Views are standard Go HTML templates and must be stored inside the views
directory, within .gohtml
files.
They are automatically parsed during launch.
Use Context.View
to send a view:
app.Get("/", func(c *app.Context) {
c.View("home")
})
Use a ViewData
map to pass data to a view.
Note that the context is automatically stored in the view data under key c
.
You can also use GlobalViewData
to set data for all views:
app.GlobalViewData(app.ViewData{
"appName": "My app",
})
app.Get("/", func(c *app.Context) {
user := &User{
ID: 1,
Name: "John Doe",
}
c.View("home", app.ViewData{
"user": user,
})
})
In views/home.gohtml:
{{define "home"}}
<h1>Hello {{.user.Name}} ({{.c.Req.RemoteAddr}}) and welcome on {{.appName}}!</h1>
{{end}}
Use GlobalViewFuncs
to set functions for all views:
app.GlobalViewFuncs(app.ViewFuncs{
"pathescape": url.PathEscape,
})
app.Get("/posts/new", func(c *app.Context) {
c.View("postsNew")
})
In views/posts.gohtml:
{{define "postsNew"}}
<a href="/sign-in?return-to={{pathescape "/posts/new"}}">Sign in</a>
{{end}}
These function are available out of the box:
Function | Description | Usage |
---|---|---|
t |
Returns the translation associated to key, for the client locale. | {{t .c "hello"}} |
thtml |
Works like t but returns an HTML unescaped translation. nl2br is applied to the result. |
{{t .c "hello"}} |
tn |
Works like t with plural variations (zero, one, other). See Context.Tn . |
{{tn .c "item" 12}} |
tnhtml |
Works like tn + thml . See Context.TnHTML . |
{{tnhtml .c "item" 12}} |
fmtn |
Returns a formatted number with decimal and thousands marks. | {{fmtn 123456.123456}} |
safehtml |
Returns a string that will not be unescaped. Be carefull. | {{safehtml "<strong>word</strong>"}} |
nl2br |
Converts \n to HTML <br> . |
{{nl2br "line one\nline two"}} |
styles |
Sets an HTML link to the given stylesheets. | {{styles "/static/main.css" "/static/user.css"}} |
scripts |
Sets an HTML link to the given scripts. | {{scripts "/static/main.js" "/static/user.js"}} |
googlefonts |
Sets an HTML link to Google Fonts's stylesheet of the given fonts. | {{googlefonts "Open+Sans:400,700|Spectral"}} |
envproduction |
Tells if the app is run with the production flag (returns a bool ). |
{{if envproduction}}Live{{else}}Testing{{end}} |
Static files must be stored inside the static
directory.
They are automatically accessible from the /static/
path prefix.
Call Run
at the end of your main function:
app.Run()
By default, your app will listen and serve on :8080
.
But you can change this address by using flag -a
when running your app:
./myapp -a :1234
Custom middlewares can be used if they are compatible with standard interface net/http.Handler
.
They can be set for:
-
The entire app:
app.Run(hand1, hand2, hand3)
-
A group:
api := app.Group("/api", hand1, hand2, hand3)
-
A single route:
api := app.Get("/", func(c *app.Context) { // Write response for GET / }, hand1, hand2, hand3)
First handler wraps the second and so on.