README ¶
Gopher
Gopher is a modern, light weight, and extensible web framework for Go
Route.Get("/", func(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "Hello, Gopher!")
})
ListenAndServe()
> go run server.go
INFO[0000] |----------------------------------------|
INFO[0000] | _____
INFO[0000] | / ____| | |
INFO[0000] | | | __ ___ _ __ | |__ ___ _ __
INFO[0000] | | | |_ |/ _ \| '_ \| '_ \ / _ \ '__|
INFO[0000] | | |__| | (_) | |_) | | | | __/ |
INFO[0000] | \_____|\___/| .__/|_| |_|\___|_|
INFO[0000] | | |
INFO[0000] | |_|
INFO[0000] |----------------------------------------|
INFO[0000] | GOPHER READY FOR ACTION ON PORT 3000
INFO[0000] |----------------------------------------|
> curl http://localhost:3000/
Hello, Gopher!
Want live code reload? Use either gin or fresh to live reload Gopher apps.
Table of Contents
- Overview
- Heroic Features
- Getting Started
- The Basics
- Roadmap
- Frequently Asked Questions
- Support
- Contribution Guide
- Authors
- License
Overview
//TODO
Heroic Features
- Simple: Straightforward, clean Idiomatic Go syntax.
- Intuitive: Beautiful APIs for maximum coding happiness and productivity.
- Exposed: No reflection, dependency injection, or hidden magic. Just clean Go interfaces.
- Modern: Features an IoC Container, nested Middleware, flexible Routing, and more.
- Extensible: Easy to add Service Providers or even replace the built-in ones.
- Comprehensive: Routing, Handlers, Middleware, Logging, Views, and much more.
- Speedy: Gopher is blazing fast. See our benchmarks.
- Documented: Thoroughly detailed APIs
Getting Started
Let's create our first "Hello, Gopher!" example.
1. Install Gopher
- Install Go and set your GOPATH (if you haven't already).
- Then, from your GOPATH, type this to install Gopher and its dependencies:
go get github.com/gopherlabs/gopher
2. Create your server.go file
package main
import (
"net/http"
. "github.com/gopherlabs/gopher"
)
func main() {
Route.Get("/", func(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "Hello, Gopher!")
})
ListenAndServe()
}
See this example at: routes/01_hello.go
NOTE: Only for the purpose of syntax clarity, the example above uses the dot import notation as in:
import . "github.com/gopherlabs/gopher"
It should be noted, however, that the Go team does not recommend using the dot import since it can cause some odd behaviour in certain cases.
3. Run your server
go run server.go
You will now have a Gopher web server running on localhost:3000 (default port) and you should see the following output:
INFO[0000] |----------------------------------------|
INFO[0000] | LOADING SERVICE PROVIDERS ...
INFO[0000] |----------------------------------------|
INFO[0000] | * LOGGER ✓
INFO[0000] | * MAPPER ✓
INFO[0000] | * ROUTER ✓
INFO[0000] | * RENDERER ✓
INFO[0000] |----------------------------------------|
INFO[0000] | _____
INFO[0000] | / ____| | |
INFO[0000] | | | __ ___ _ __ | |__ ___ _ __
INFO[0000] | | | |_ |/ _ \| '_ \| '_ \ / _ \ '__|
INFO[0000] | | |__| | (_) | |_) | | | | __/ |
INFO[0000] | \_____|\___/| .__/|_| |_|\___|_|
INFO[0000] | | |
INFO[0000] | |_|
INFO[0000] |----------------------------------------|
INFO[0000] | GOPHER READY FOR ACTION ON PORT 3000
INFO[0000] |----------------------------------------|
4. Now, try it!
> curl http://localhost:3000/
Hello, Gopher!
Awesome, it worked!
Next, let's take a look at some of the basic concepts of Gopher:
The Basics
Routing
Routing Overview
You will define the routes for your application using the Route instance, which is satisfies the Routable interface.
The most basic Gopher routes simply accept a URI and a Closure as in:
Route.Get("/", func(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "Hello, Gopher!")
})
Request Handlers
Although you can use Closures as in the example above, it is often more practical to encapsulate the request handling logic in handler functions which can be reused between routes:
func main() {
Route.Get("/hello", HelloHandler)
ListenAndServe()
}
func HelloHandler(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "Hello, Handler!")
}
Route Verbs
Gopher provides routing methods to handle every specific http verb:
func main() {
Route.Get("/products", VerbHandler)
Route.Post("/form", VerbHandler)
Route.Put("/update", VerbHandler)
Route.Delete("/etc", VerbHandler)
Route.Head("/etc", VerbHandler)
Route.Options("/etc", VerbHandler)
ListenAndServe()
}
func VerbHandler(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "Hello, "+r.Method)
}
Multiple Verbs
Sometimes you may need to register a route that responds to multiple HTTP verbs. You may do so using the Match() method of the Route Facade as shown on this example:
Route.Match("/hello", MatchHandler, []string{"GET", "POST", "PUT"})
Or, you may even register a route that responds to all HTTP verbs using the All() method:
Route.All("/", AllHandler)
Route Variables
Sometimes you will need to capture segments of the URI within your route. You may do so by defining route variables. Route variables are always encased within "curly" braces. They are defined using the format {name} or {name:pattern}
For example, you may need to capture a user's ID from the URL:
Route.Get("user/{id}", func(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "User: "+Route.Var(r, "id"))
})
You may define as many route parameters as required by your route:
Route.Get("posts/{post}/comments/{comment}", func(w http.ResponseWriter, r *http.Request) {
// The entire map of route variables can be retrieved calling Route.Vars()
})
Route Groups
Route groups allow you to share route attributes (such as path prefixes, hosts, methods, etc) across a large number of routes without needing to define those attributes on each individual routes.
RouteGroup.New(matcher GroupMatcher) Routable
Shared attributes are passed as type GroupMatcher
as the first parameter to RouteGroup.New()
.
type GroupMatcher struct {
Host string
PathPrefix string
Methods []string
Queries []string
Schemes []string
}
Route Prefixes
The PathPrefix
attribute may be used to prefix each route in the group with a given URI.
For example, you may want to prefix all route URIs within the group with /products:
group := RouteGroup.New(GroupMatcher{
PathPrefix: "/products",
})
group.Get("/item", func(rw http.ResponseWriter, req *http.Request) {
Render.Text(rw, "Hello Item!")
})
In this example, the /item route inherits all the route attributes of its parent RouteGroup, such as its path prefix:
> curl http://localhost:3000/products/item
Hello Item!
Serving Static Files
Serving files, such as images, CSS, JavaScript and other static files is accomplished with the help
of the Route.Static()
API.
Route.Static(path string, dir string)
Where:
path
is path prefix for the files served by Gopher.dir
the name of the directory, which is to be used as the location of static assets.
For example, if you keep your images, CSS, and JavaScript files in a directory named public, you can do this:
Route.Static("/static", "./public")
Now, you will be able to load the files under the public directory, from the path prefix "/static".
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
Not Found Route
Using Route.NotFound()
, you may register an error handler that handles all "404 Not Found" errors
in your application, allowing you to easily return custom 404 error pages, or execute any code you want.
Route.NotFound(func(rw http.ResponseWriter, req *http.Request) {
Render.Text(rw, "Could not find page")
})
Routing Configuration
How do I change the port/host?
Gopher's ListenAndServe() function defaults to using HOST value of 0.0.0.0 and PORT 3000.
An easy way to change those values is to set PORT and HOST environment variables before running Gopher like this:
PORT=8080 HOST=localhost go run server.go
> curl http://localhost:8080/
Hello, Gopher!
If you don't want to set the PORT and HOST environment variables you can also configure those values using the App.Config() API as shown below:
App.Config(Config{
KEY_ROUTER: ConfigRouter{
Port: 8080,
Host: "localhost",
StaticDirs: map[string]string{
"/static": "./static/",
},
},
})
Otherwise, if you want more flexibility over the way you start your app, use the GetHttpHandler() function instead, which returns the built-in http.Handler:
http.ListenAndServe("localhost:8080", GetHttpHandler())
Middleware
In Gopher, a Middleware is a function with access to the request object (http.Request), the response object (http.ResponseWriter), the next middleware in the application's request-response cycle, commonly denoted by a function argument named next, which accepts a variadic number or arguments satisfying the MiddlewareHandler type.
func(w http.ResponseWriter, r *http.Request, next func(), args ...interface{})
Gopher Middleware can:
- Execute any code.
- Make changes to the request and the response objects.
- End the request-response cycle.
- Call the next middleware in the stack.
If the current middleware does not end the request-response cycle, it must call next() to pass control to the next middleware, otherwise the request will be left hanging.
A Gopher application can use the following kinds of middleware:
- Application-level Middleware
- Router-level Middleware
- RouteGroup-level Middleware
- Route-level Middleware
- Built-in Middleware
Middleware & the Request-Response Lifecycle
//TODO
Application-level Middleware
//TODO
Router-level Middleware
RouteGroup-level Middleware
//TODO
Route-level Middleware
//TODO
Built-in Middleware
//TODO
Context
Context.Set("user", "Ricardo")
Route.Get("/user", func(w http.ResponseWriter, r *http.Request) {
Render.Text(w, "Hello, "+Context.Get("user").(string))
})
type Mappable interface {
Get(key string) interface{}
Has(key string) bool
Set(key string, value interface{})
Remove(key string)
}
Logging
Logging Overview
Gopher has six logging levels: Debug, Info, Warning, Error, Fatal and Panic:
Log.Debug("Useful debugging information.")
Log.Info("Something noteworthy happened!")
Log.Warn("You should probably take a look at this.")
Log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
Log.Fatal("Bye.")
// Calls panic() after logging
Log.Panic("I'm bailing.")
Log Levels
You can set the logging level on using the global App.Config() API, so it will only log entries with that severity or anything above it:
App.Config(Config{
KEY_LOGGER: ConfigLogger{LogLevel: LEVEL_INFO},
})
// Debug logs will not be logged since we set the Log Level to LEVEL_INFO
Log.Debug("Useful debugging information.")
// Anything with severity Info or above it will be logged
Log.Info("Something noteworthy happened!")
Log.Warn("You should probably take a look at this.")
Log.Error("Something failed but I'm not quitting.")
Logging Configuration
Besides configuring the log level, you can also specify the time stamp format for the logging output by setting the ConfigLogger.TimestampFormat attribute as shown below:
App.Config(Config{
KEY_LOGGER: ConfigLogger{
TimestampFormat: time.RFC822,
LogLevel: LEVEL_INFO,
},
})
Views & Templates
//TODO
Responses
//TODO
[](ArchitectureIoC ContainerContractsFacadesService Providers)
Roadmap
v0.9
- Routing APIs
- Request Handlers
- Nested Middleware
- Application Context
- Logging
- Views & Templates
- Responses
- IoC Container
- Contracts
- Facades
- Initial Documentation
v1.0
- Extensibility APIs
- Performance Benchmarks
- Enhanced Documentation
- Enhanced Test Cases
v2.0
- DB/ORM
- Sessions
- Caching
- Hashing
- Authentication
- Queues?
v3.0
- Micro-Gopher Docker Containers
- Health & Service Instrumentation
- LTS Support Options
Frequently Asked Questions
-
This looks great and I would like to start using Gopher right now, but how stable is it?
Gopher is currently in alpha (pre v0.9 release) but by all means use it now! We don't have any planned breaking API changes (although we can't guarantee it won't happen) from now until the the v1.0 release as we are mainly focusing on performance, testing, bug fixes, and extensibility until then.
-
Is anyone using Gopher yet?
At Gopher Labs, we are using Gopher for several customer projects. If you, or anyone you know is using Gopher please let us know and we can include them here.
-
Will you provide Long-term support (LTS)?
Yes, LTS options are planned for Gopher v3 (Target Release H1 2016). Please see our Roadmap for details.
Support
Forum: https://groups.google.com/d/forum/gopher-framework
Mailing List: gopher-framework@googlegroups.com
Twitter: For Gopher announcements, follow us at @gopherweb
Chat: Join the conversation at
Contribution Guide
Thank you for considering contributing to Gopher!
To encourage active collaboration, we strongly encourage pull requests, not just bug reports. "Bug reports" may also be sent in the form of a pull request containing a failing test.
However, if you file a bug report, your issue should contain a title and a clear description of the problem. You should also include as much relevant information as possible and a code sample that demonstrates the issue. The goal of a bug report is to make it easy to replicate the bug and develop a fix.
Opening a Pull Request
- Fork the appropriate Gopher repository: gopher, gopher-framework, gopher-services
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Authors
Created with ♥ by Ricardo Rossi @ricardojrossi, founder of Gopher Labs ✉ ricardo@gopherlabs.org
Maintained with care by all its Contributors
Built-in Service Providers include the following vendored projects:
- gorilla/mux gorilla/mux
- sirupsen/logrus https://github.com/sirupsen/logrus
- unrolled/render https://github.com/unrolled/render
License
The Gopher framework is open-sourced software licensed under the MIT license
Copyright (c) 2015 Ricardo Rossi (Gopher Labs) - ricardo@gopherlabs.org
Thank you for using Gopher!
We hope you enjoy using Gopher. Please Star it, Watch it, & Share this repo.
Follow us on Twitter @gopherweb
_____
/ ____| | | .. .. . ..
| | __ ___ _ __ | |__ ___ _ __ . .?8MMMMMNOZ$ZONMMMN~..
| | |_ |/ _ \| '_ \| '_ \ / _ \ '__| . 8M8~. .OMI. . ... .
| |__| | (_) | |_) | | | | __/ | . . ...MD . .. . .. 7M ?MMMMMO
\_____|\___/| .__/|_| |_|\___|_| MM:. :DNM ...MI.. 8Z. +I. .. N :M ?M
| | :M . M.. ?. .M $ . = .M ~, ?.
|_| M. .7IM . $. .7 . MMM. 8.
_ _ _ .M 8MM +..... M ~ =MMN D. MM .I.
| | | | | | .M .Z: ~ MMMMN. Z+MMMMM. D M .M.
| | | | ___| |__ . M .M. . MMM.M ~ MMMII. D Z,M7
| |/\| |/ _ \ '_ \ .OMO$. .O,MMN .M D. . . M..
\ /\ / __/ |_) | M .8. M .++:.O . $. M .
\/ \/ \___|_.__/ M .D . .=, MMMMMM. 77. .. M ?7
______ _ .,8 .8MNN7 ,MMMMMMO8?. ... . .M
| ___| | | .$= M .. ... .. M
| |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ N N . . . = M
| _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / .M. $OO..N ?MD= M
| | | | | (_| | | | | | | __/\ V V / (_) | | | < M . . 7 N M
\_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ .M. . . .N N M.
Documentation ¶
Index ¶
Constants ¶
const ( KEY_LOGGER = f.LOGGER KEY_ROUTER = f.ROUTER KEY_RENDERER = f.RENDERER KEY_CONTEXT = f.CONTEXT )
const ( // PanicLevel level, highest level of severity. Logs and then calls panic with the // message passed to Debug, Info, ... LEVEL_PANIC uint8 = iota // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the // logging level is set to Panic. LEVEL_FATAL // ErrorLevel level. Logs. Used for errors that should definitely be noted. // Commonly used for hooks to send errors to an error tracking service. LEVEL_ERROR // WarnLevel level. Non-critical entries that deserve eyes. LEVEL_WARN // InfoLevel level. General operational entries about what's going on inside the // application. LEVEL_INFO // DebugLevel level. Usually only enabled when debugging. Very verbose logging. LEVEL_DEBUG )
Log Levels
Variables ¶
var ( App *app Log f.Loggable Route f.Routable RouteGroup routeGroup Context f.Contextable Render f.Renderable )
Functions ¶
func GetHttpHandler ¶
func ListenAndServe ¶
func ListenAndServe()
Types ¶
type ConfigLogger ¶
type ConfigLogger f.ConfigLogger
type ConfigRenderer ¶
type ConfigRenderer f.ConfigRenderer
type ConfigRouter ¶
type ConfigRouter f.ConfigRouter