As of December 1st, 2019 Buffalo, and all related packages, require Go Modules and the use of the GOPATH is no longer supported.

Please see this blog post for more information https://blog.gobuffalo.io/the-road-to-1-0-requiring-modules-5672c6b015e5.

Routage

Buffalo utilise le paquet github.com/gorilla/mux pour gérer le routage au sein des applications Buffalo. L'API de mux est néanmoins embarquée dans celle de Buffalo. Dans ce chapitre, vous allez apprendre tout ce qu'il y a à savoir sur les routes et Buffalo.

Créer une nouvelle application Buffalo (et son routeur)

La configuration de l'app se trouve dans le fichier app.go.

a := buffalo.New(buffalo.Options{
  Env:         ENV,
  SessionName: "_coke_session",
})

La configuration par défaut devrait satisfaire la plupart de vos besoins, mais vous êtes libre de la modifier pour mieux y répondre.

La liste des options est disponible ici : https://godoc.org/github.com/gobuffalo/buffalo#Options

Associer des actions

Toutes les routes dans Buffalo sont branchées à des fonctions de type buffalo.Handler. La signature d'une telle fonction ressemble à ceci :

func (c buffalo.Context) error {
  // do some work
}

Si vous connaissez déjà le pattern MVC, les fonctions buffalo.Handler gèrent la partie contrôleur : c'est l'endroit où l'on gère tout l'aspect logique de l'application. Les fonctions buffalo.Handler prennent en paramètre une struct buffalo.Context, qui contient tout le nécessaire sur la requête courante.

Consultez la page Contexte pour mieux comprendre l'interface buffalo.Context.

Méthodes HTTP supportées

Buffalo supporte de base les méthodes HTTP suivantes :

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE
  • OPTIONS
  • HEAD

Vous pouvez également gérer toutes les méthodes HTTP d'un coup en utilisant ANY.

Pour associer un buffalo.Handler à une méthode HTTP, on procède de la sorte :

a.GET("/some/path", SomeHandler)
a.POST("/some/path", func (c buffalo.Context) error {
  // do some work
})
// etc...

Comme vous pouvez le voir, il est possible d'utiliser des fonctions en ligne directement dans la déclaration de la route. Pour rendre les choses plus lisibles, il est toutefois préférable de séparer vos buffalo.Handler des routes et de les placer dans plusieurs fichiers. Par exemple, si vous avez plusieurs buffalo.Handler qui gèrent les utilisateurs, vous pouvez les grouper dans un fichier users.go dans le dossier actions.

Routes nommées

Par défaut, Buffalo donne un nom à chacune de vos routes, sous la forme pathnamePath. Par exemple a.GET("/coke", CokeHandler) donne le nom de route cokePath.

a.GET("/coke", CokeHandler) // cokePath()

Ces noms deviennent ceux des helpers utilisables dans vos templates.

<a href="<%= cokePath() %>">Coke</a>

Vous pouvez obtenir la liste de toutes vos routes en exécutant la commande buffalo routes.

$ buffalo routes

METHOD | PATH                       | ALIASES | NAME           | HANDLER
------ | ----                       | ------- | ----           | -------
GET    | /                          |         | rootPath       | github.com/markbates/coke/actions.HomeHandler
GET    | /widgets/                  |         | widgetsPath    | github.com/markbates/coke/actions.WidgetsResource.List
POST   | /widgets/                  |         | widgetsPath    | github.com/markbates/coke/actions.WidgetsResource.Create
GET    | /widgets/new/              |         | newWidgetsPath | github.com/markbates/coke/actions.WidgetsResource.New
GET    | /widgets/{widget_id}/      |         | widgetPath     | github.com/markbates/coke/actions.WidgetsResource.Show
PUT    | /widgets/{widget_id}/      |         | widgetPath     | github.com/markbates/coke/actions.WidgetsResource.Update
DELETE | /widgets/{widget_id}/      |         | widgetPath     | github.com/markbates/coke/actions.WidgetsResource.Destroy
GET    | /widgets/{widget_id}/edit/ |         | editWidgetPath | github.com/markbates/coke/actions.WidgetsResource.Edit

IMPORTANT : Puisque les noms des helpers de routes sont générés en utilisant le chemin de l'URL, (/widgets/new -> newWidgetsPath), si ce chemin change, le nom du helper de route change aussi.

app.Resource("/fooz", WidgetsResource{})
$ buffalo routes

METHOD | PATH                    | ALIASES | NAME         | HANDLER
------ | ----                    | ------- | ----         | -------
GET    | /                       |         | rootPath     | github.com/markbates/coke/actions.HomeHandler
GET    | /fooz/                  |         | foozPath     | github.com/markbates/coke/actions.WidgetsResource.List
POST   | /fooz/                  |         | foozPath     | github.com/markbates/coke/actions.WidgetsResource.Create
GET    | /fooz/new/              |         | newFoozPath  | github.com/markbates/coke/actions.WidgetsResource.New
GET    | /fooz/{widget_id}/      |         | foozPath     | github.com/markbates/coke/actions.WidgetsResource.Show
PUT    | /fooz/{widget_id}/      |         | foozPath     | github.com/markbates/coke/actions.WidgetsResource.Update
DELETE | /fooz/{widget_id}/      |         | foozPath     | github.com/markbates/coke/actions.WidgetsResource.Destroy
GET    | /fooz/{widget_id}/edit/ |         | editFoozPath | github.com/markbates/coke/actions.WidgetsResource.Edit

Consultez Routes nommées personnalisées pour plus de détails sur comment définir les noms de routes vous-même.

Utiliser les helpers de routes dans les templates

Les helpers de toutes peuvent être directement utilisés dans les templates, en utilisant le nom du helper :

<%= widgetsPath() %> // /widgets

Les routes qui nécessitent des paramètres nommés doivent recevoir une map avec ces paramètres.

<%= editWidgetPath({widget_id: 1}) %> // /widgets/1/edit

pathFor helper

Le helper pathFor prend en paramètre une interface{}, ou une slice d'interface{}, et il essaie de le convertir en un chemin d'URL de la forme /foos/{id}.

Règles :

  • si le paramètre est une string, il est retourné tel quel.
  • si c'est un type Pathable, le résultat de la méthode ToPath est retourné.
  • si c'est une slice ou un array, chaque élément est traité en appliquant ces règles, puis le résultat est joint.
  • si c'est une struct, le nom pluralisé de la struct est utilisé pour définir le nom.
  • si c'est un type Paramable, la méthode ToParam est appelée pour remplir l'{id}.
  • si dans la struct, un attribut Slug est présent, le slug est utilisé pour remplir l'{id}.
  • si dans la struct, un attribut ID est présent, l'ID est utilisé pour remplir l'{id}.
// Car{1} => "/cars/1"
// Car{} => "/cars"
// &Car{} => "/cars"
type Car struct {
  ID int
}

// Boat{"titanic"} => "/boats/titanic"
type Boat struct {
  Slug string
}

// Plane{} => "/planes/aeroPlane"
type Plane struct{}

func (Plane) ToParam() string {
  return "aeroPlane"
}

// Truck{} => "/a/Truck"
// {[]interface{}{Truck{}, Plane{}} => "/a/Truck/planes/aeroPlane"
type Truck struct{}

func (Truck) ToPath() string {
  return "/a/Truck"
}

Utiliser les helpers de routes dans les actions

Redirections avec un helper de route

Vous pouvez également utiliser les noms de routes pour vos redirections (à la place des chemins en dur).

func MyHandler(c buffalo.Context) error {
  return c.Redirect(307, "widgetsPath()")
  // Ou avec des paramètres
  return c.Redirect(307, "widgetPath()", render.Data{"widget_id": "1"})
}

Trouver/appeler un helper de route

depuis v0.13.0-beta.1

La méthode buffalo.RouteList#Lookup vous permet de chercher une route à partir de son nom, depuis l'application. Vous obtenez alors une struct RouteInfo pour la route demandée, et cette struct vous permet de générer le chemin pour cette route.

func MyHandler(c buffalo.Context) error {
  ri, err := App().Routes().Lookup("widgetPath")
  if err != nil {
    return errors.WithStack(err)
  }
  h := ri.BuildPathHelper()
  u, err := h(render.Data{"widget_id": 1})
  if err != nil {
    return errors.WithStack(err)
  }
  return c.Redirect(307, string(u))
}

Routes nommées personnalisées

La fonction buffalo.RouteInfo#Name vous permet de donner un nom fixe et personnalisé à un helper de route.

a.GET("/coke", CokeHandler).Name("customPath")

Cette route est maintenant appelée customPath, et vous pouvez y faire référence sous ce nom dans vos templates.

<a href="<%= customPath() %>">Coke</a>

Paramètres

Les paramètres d'URL et autres paramètres de requête sont disponibles depuis le buffalo.Context qui est passé au buffalo.Handler.

a.GET("/users", func (c buffalo.Context) error {
  return c.Render(200, r.String(c.Param("name")))
})

Si l'on prend l'exemple ci-dessus : en appelant la route GET /users?name=ringo, la réponse devrait être 200: ringo.

Paramètres nommés

Puisque le routeur Buffalo n'est rien d'autre qu'une version « habillée » du routeur github.com/gorilla/mux, vous pouvez accéder à toutes les fonctionnalités du routeur gorilla/mux. Par exemple, vous pouvez utiliser des pseudo-expressions régulières dans vos routes pour définir des paramètres nommés. Ils seront alors disponibles via le buffalo.Context.

a.GET("/users/{name}", func (c buffalo.Context) error {
  return c.Render(200, r.String(c.Param("name")))
})

Dans l'exemple ci-dessus, si l'on appelle la route GET /users/ringo, la réponse devrait être 200: ringo.

a.GET("/users/new", func (c buffalo.Context) error {
  return c.Render(200, r.String("new"))
})
a.GET("/users/{name}", func (c buffalo.Context) error {
  return c.Render(200, r.String(c.Param("name")))
})

Vous devriez pouvoir créer des chemins similaires, comme /users/new et /users/{name} sans problème. Le routeur s'assurera de les mener au bon endroit.

Expressions régulières

github.com/gorilla/mux permet d'utiliser des expressions régulières dans les URLs. Vous pouvez donc pré-filtrer les requêtes :

a.GET("/articles/{id:[0-9]+}", func (c buffalo.Context) error {
  return c.Render(200, r.String(c.Param("id")))
})

Groupes

Buffalo permet de grouper des routes ensemble. Cela permet de partager des fonctionnalités communes, telles que l'utilisation de middlewares. Un bon exemple serait une racine d'API.

g := a.Group("/api/v1")
g.Use(APIAuthorizer)
g.GET("/users", func (c buffalo.Context) error {
  // répond à GET /api/v1/users
})

Par défaut, un groupe de routes hérite de tous les middlewares de son application parente.

a.Use(SomeMiddleware)
g := a.Group("/api/v1")
g.Use(APIAuthorizer)

Dans l'exemple ci-dessus, le groupe /api/v1 utilisera les middlewares SomeMiddleware et APIAuthorizer. Consultez la page Middleware pour plus d'informations sur l'utilisation des middlewares.

Connecter des applications http.Handler

depuis v0.9.4

Parfois, vous souhaiterez réutiliser certains composants d'autres applications. En utilisant la méthode Mount, vous pouvez connecter un http.Handler standard à une route, tout comme vous le feriez avec une action Buffalo.

func muxer() http.Handler {
  f := func(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(res, "%s - %s", req.Method, req.URL.String())
  }
  mux := mux.NewRouter()
  mux.HandleFunc("/foo/", f).Methods("GET")
  mux.HandleFunc("/bar/", f).Methods("POST")
  mux.HandleFunc("/baz/baz/", f).Methods("DELETE")
  return mux
}

a.Mount("/admin", muxer())

Puisque les applications Buffalo implémentent l'interface http.Handler, vous pouvez également connecter une autre application Buffalo et construire des applications modulaires.