Goroutes is a url routing library for go with support for routing RESTful routes to controllers.
This started a with me wanting to experiment with go, but i have been developing using rails for the past months and a quickly missed a lot of its features. Url routing with variables and RESTful routes to controllers were one of the major features i missed so i decided to build this RESTful routing library for go.
It takes inspiration from rails as well as the standard go url routing found in net/http.
First thing you have to do is get the project
go get github.com/emilsjolander/goroutes
After that all you need to do is import it into your project
import "github.com/emilsjolander/goroutes"
Done! Now just match some routes and press Start! The server will run on port 9999. Test it out locally at http://localhost:9999/
goroutes.Resources(new(UsersController))
goroutes.Resources(new(NotesController), "UsersController")
goroutes.MatchFunc("GET", "/status",
func(w http.ResponseWriter, req *http.Request){
fmt.Fprintf(w, "Status ok!")
})
goroutes.Match("GET", "/assets/*", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets"))))
goroutes.Match("GET", "/", new(HomeHandler))
goroutes.Start()
I have designed the api to be as similar as possible to go's routing while at the same time adding as many of the great routing features that come with a framework like rails.
###Controllers This is the Controller interface. If your datastructure implements this interface goroutes can match RESTful routes the the corrosponding methods.
type Controller interface {
Index (w http.ResponseWriter, req *http.Request)
New (w http.ResponseWriter, req *http.Request)
Create (w http.ResponseWriter, req *http.Request)
Show (w http.ResponseWriter, req *http.Request)
Edit (w http.ResponseWriter, req *http.Request)
Update (w http.ResponseWriter, req *http.Request)
Destroy (w http.ResponseWriter, req *http.Request)
}
A lot of times a controller will only want to implement a handful of these methods, for this i have defined a BaseController struct that can be included into any controller as an anonymous field. Any method not overriden in your struct will answer the request with a 404.
type MyController struct {
goroutes.BaseController
}
func (c *MyController) Index(w http.ResponseWriter, req *http.Request) {
// my response
}
To generate resources for a controller call
goroutes.Resources(new(ExampleController))
This call will generate the following resources
GET /example (Index)
GET /example/new (New)
POST /example (Create)
GET /example/:Id (Show)
GET /example/:Id/edit (Edit)
PUT /example/:Id (Update)
DELETE /example/:Id (Destroy)
If any number of parent controllers were given that would prefix every url pattern with /parents/:ParentId, this can be achieved by the following method call.
goroutes.Resources(new(ExampleController), "ParentsController")
Multiple parent controllers are also supported
goroutes.Resources(new(ExampleController), "ParentsController", "GrandparentsController")
The preceding call would generate the following resources.
GET /grandparents/:GrandparentId/parents/:ParentId/example (Index)
GET /grandparents/:GrandparentId/parents/:ParentId/example/new (New)
POST /grandparents/:GrandparentId/parents/:ParentId/example (Create)
GET /grandparents/:GrandparentId/parents/:ParentId/example/:Id (Show)
GET /grandparents/:GrandparentId/parents/:ParentId/example/:Id/edit (Edit)
PUT /grandparents/:GrandparentId/parents/:ParentId/example/:Id (Update)
DELETE /grandparents/:GrandparentId/parents/:ParentId/example/:Id (Destroy)
###Controller naming
A Controller's name must end with 'Controller' e.g. UsersController
, InfoController
and so on. A controller with a plural name such as UsersController
will have it's id parameter singularized when accessed via a child controller, a child controller of UsersController
would look for a UserId
param that is. Controller names made up of multiple words such as SuperUserController
will become /super_user
in the url.
###Before filter Often time you will want to do a certain thing for many, if not all the actions in a controller. One example of this would be authentication. To make sure you do not repeat yourself your controller should implement the BeforeFilterer interface. In the BeforeFilter method you can deal will everything that you would otherwise repeat in many action methods. Save any data in the controller struct fields so they can be accessed from the action method which is called after.
type BeforeFilterer interface{
BeforeFilter(a Action, w http.ResponseWriter, r *http.Request) bool
}
This method will be called just before the action method is called. Returning false from this method will result if the action method not being called, returning true will call the action method directly after the before filter. The action sent to this method is one of the following defined in controller.go.
type Action uint
const (
Index Action = iota
New
Create
Show
Edit
Update
Destroy
)
###Single function routing Controllers are not always the correct solution so there are two more methods for routing urls. They are really the same method only one takes in a struct implementing http.Handler and the other a handler function.
func Match(method string, pattern string, handler http.Handler) error
func MatchFunc(method string, pattern string, handler func(http.ResponseWriter, *http.Request)) error
They expect a http method (GET, POST, PUT or DELETE) or a empty string to indicate that the handler will handle all methods. The pattern sent in can contain variables which are preceded with a ':'. The last segment or the url may also be the wildcard character '*'. The wilcard will match anything after it while the variables will only match anything in the corresponding segment.
###Extracting url values Pattern variables are extracted from the Request the same way form and query params are extracted.
func (c *ExampleController) Show(w http.ResponseWriter, req *http.Request) {
req.ParseForm()
id := req.Form["Id"][0]
parentId := req.Form["ParentId"][0]
}
Remember that if the controllers parent name is plural, the id name will be singular.
###Namespaces Goroutes makes it easy to namespace your resources. You can wrap any of the above function calls in a call to the Namespace function.
func Namespace(ns string, f func())
ns
is the namespace you want to route inside and f
is the function where all route matching for this namespace should occur.
Calls to the Namespace function can be nested. Here is an example of this.
goroutes.Namespace("api", func(){
goroutes.Match("GET", "/info", infoHandler)
goroutes.Namespace("v1", func(){
goroutes.Match("GET", "/login", loginHandler)
})
})
The above code generates the following routes.
GET /api/info
GET /api/v1/login
Currently goroutes is not thread safe during the initialization process. This means that until this limitation is fixed you should do all setup in a single goroutine, the main function is a very good place for this.
Pull requests and issues are very welcome!
Feature request are also welcome but i can't make any promise that they will make it in. I would like to keep the library as general as possible, if you are unsure you can just ask before you code ;)
Copyright 2013 Emil Sjölander
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.