- Notifications
You must be signed in to change notification settings - Fork3
🚀 Full featured HTTP framework for web apps
License
gowww/app
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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, askContext.PathValue.
It will return the value as a string (empty if the parameter doesn't exist).
Example, with a parameterid
:
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 aregular 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, seegowww/router.
A routing group works like the top-level router but prefixes all subroute paths:
api:=app.Group("/api"){v1:=api.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:=api.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 withNotFound:
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) withError and retrieve the recovered error value withContext.Error:
app.Error(func(c*app.Context) {c.Status(http.StatusInternalServerError)ifc.Error()==ErrCannotOpenFile {c.View("errorStorage")return}c.View("error")})
AContext is always used inside aHandler.
It contains the original request and response writer but provides all the necessary helpers to access them:
UseContext.Req to access the original request:
app.Get("/",func(c*app.Context) {r:=c.Req})
UseContext.FormValue to access a value from URL or body.
You can also useContext.HasFormValue to check its existence:
app.Get("/",func(c*app.Context) {ifc.HasFormValue("id") {id:=c.FormValue("id")}})
UseContext.Res to access the original response writer:
app.Get("/",func(c*app.Context) {w:=c.Res})
UseContext.Text orContext.Bytes to send a string:
app.Get("/",func(c*app.Context) {c.Text("Hello")c.Bytes([]byte("World"))})
UseContext.JSON to send a JSON formatted response (if implemented by argument,JSON() interface{}
will be used):
app.Get(`/users/:id:^\d+$/files/`,func(c*app.Context) {c.JSON(map[string]interface{}{"userID":c.PathValue("id"),"filepath":c.PathValue("*"),})})
UseContext.Status to set the response status code:
app.Get("/",func(c*app.Context) {c.Status(http.StatusCreated)})
UseContext.NotFound to send a "not found" response:
app.Get("/",func(c*app.Context) {c.NotFound()})
UseContext.Panic to log an error and send a "serving error" response:
app.Get("/",func(c*app.Context) {c.Panic("database connection failed")})
UseContext.Redirect to redirect the client:
app.Get("/old",func(c*app.Context) {c.Redirect("/new",http.StatusMovedPermanently)})
UseContext.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).
UseContext.Set to set a value:
app.Get("/",func(c*app.Context) {c.Set("clientCountry","UK")})
UseContext.Get to retrieve a value:
app.Get("/",func(c*app.Context) {clientCountry:=c.Get("clientCountry")})
Views are standardGo HTML templates and must be stored inside theviews
directory.
They are automatically parsed during launch.
UseContext.View to send a view:
app.Get("/",func(c*app.Context) {c.View("home")})
Use aViewData map to pass data to a view.
You can also useGlobalViewData 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,})})
Inviews/home.gohtml:
{{define "home"}}<h1>Hello {{.user.Name}} ({{.c.Req.RemoteAddr}}) and welcome on {{.appName}}!</h1>{{end}}
This data is always passed to the views, out of the box:
Data | Description |
---|---|
.c | The currentContext. |
.envProduction | Tells if the app is run with the production flag. |
.errors | Seevalidation. |
UseGlobalViewFuncs to set functions for all views:
app.GlobalViewFuncs(app.ViewFuncs{"pathescape":url.PathEscape,})app.Get("/posts/new",func(c*app.Context) {c.View("postsNew")})
Inviews/posts.gohtml:
{{define "postsNew"}}<ahref="/sign-in?return-to={{pathescape"/posts/new"}}">Sign in</a>{{end}}
In addition to the functions provided by the standardtemplate package, these function are also available out of the box:
Function | Description | Usage |
---|---|---|
asset | Appends the file hash to the name of a static file from thestatic directory. | {{asset "videos/loop.mp4"}} |
googlefonts | Sets HTML tag forGoogle Fonts stylesheet and given font(s). | {{googlefonts "Open+Sans:400,700|Spectral"}} |
nl2br | Converts\n to HTML<br> . | {{nl2br "line one\nline two"}} |
safehtml | Prevents string to be escaped. Be careful. | {{safehtml "<strong>word</strong>"}} |
script | Sets HTML tag for a script from thestatic/script directory. | {{script "main.js"}} |
style | Sets HTML tag for a stylesheet from thestatic/style directory. | {{style "main.css"}} |
Validation is handled bygowww/check.
Firstly, make aChecker withrules for keys:
userChecker:= check.Checker{"email": {check.Required,check.Email,check.Unique(db,"users","email","?")},"phone": {check.Phone},"picture": {check.MaxFileSize(5000000),check.Image},}
The rules order is significant so for example, it's smarter to check the format of a value before its uniqueness, avoiding some useless database requests.
UseContext.Check to check the request against a checker:
errs:=c.Check(userChecker)
UseErrors.Empty orErrors.NotEmpty to know if there are errors and handle them like you want.
You can also translate error messages withContext.TErrors:
iferrs.NotEmpty() {c.Status(http.StatusBadRequest)c.View(view, app.ViewData{"errors":errs})return}
But usually, when a check fails, you only want to send a response with error messages.
Here comes theBadRequest shortcut which receives a checker and a view name.
If you don't provide a view name (empty string), the response will be a JSON errors map.
If the check fails, it sets the status to "400 Bad Request", sends the response (view or JSON) and returnstrue
, allowing you to exit from the handler:
app.Post("/join",func(c*app.Context) {ifc.BadRequest(userChecker,"join") {return}// Handle request confidently})
In views, you can retrieve theTranslatedErrors map under keyerrors
which is nevernil
in view data:
<inputtype="email"name="email"value="{{.email}}">{{if .errors.Has "email"}}<divclass="error">{{.errors.First "email"}}</div>{{end}}
Internationalization is handled bygowww/i18n.
Firstly, make your translations map (string to string, for each language):
locales:= i18n.Locales{language.English: {"hello":"Hello!",},language.French: {"hello":"Bonjour !",},}
UseLocalize to register it and set the default locale (used as a fallback):
app.Localize(locales,language.English)
MethodsContext.T,Context.Tn,Context.THTML andContext.TnHTML are now operational.
As theContext is always part of the view data, you can use these methods in views:
<h1>{{.c.T "hello"}}</h1>
Static files must be stored inside thestatic
directory.
They are automatically accessible from the/static/
path prefix.
CallRun 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 interfacenet/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.
About
🚀 Full featured HTTP framework for web apps