Skip to content

ijsnow/app

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

54 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gowww/app

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.

Start

  1. Install Go

  2. Install gowww/app:

    go get github.com/gowww/app
  3. Import it in your new app:

    import github.com/gowww/app

Routing

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 /
})

Path parameters

Named

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)
}))

Regular expressions

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.

Wildcard

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.

Groups

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 })
	}
}

Errors

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")
})

Context

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:

Request

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")
	}
})

Response

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)
})

Values

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")
})

Internationalization

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

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")
})

Data

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}}

Functions

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}}

Built-in

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

Static files must be stored inside the static directory.
They are automatically accessible from the /static/ path prefix.

Running

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

Middlewares

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.



GoDoc Build Coverage Go Report

About

[WIP] Full featured HTTP framework for web apps

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 100.0%