Gestión de Peticiones
Enrutamiento
Buffalo utiliza el paquete github.com/gorilla/mux para manejar el enrutamiento dentro de las aplicaciones de Buffalo. Dicho esto, Buffalo envuelve la API mux
con la suya propia. Esta guía te ayudará a conocer todo lo que necesita saber sobre cómo Buffalo maneja el enrutamiento.
Necesitamos tener la configuración de buffalo.App creada
La configuración de la aplicación se encuentra en el archivo actions/app.go
.
// actions/app.go
app = buffalo.New(buffalo.Options{
Env: ENV,
SessionName: "_coke_session",
})
Por defecto, Buffalo sólo requiere 2 parámetros para la configuración de su aplicación:
Env
: El entorno donde se ejecutará la aplicación. Valor por defecto:development
.SessionName
: Es la cookie de sesión que se establece. Valor por defecto:_buffalo_session
.
Puedes personalizarlo para adaptarlo a tu caso de uso.
Puedes consultar la lista de opciones disponibles aquí: https://godoc.org/github.com/gobuffalo/buffalo#Options
Buffalo.Handler
Si ya conoces el patrón MVC, las funciones de buffalo.Handler
gestionan la parte del Controlador. Su estructura es la siguiente:
func (c buffalo.Context) error {
// do some work
}
Aquí es donde va toda la lógica de la aplicación. El handler toma un parámetro buffalo.Context
, que contiene todo lo que necesitas sobre la petición actual.
buffalo.Context
.
Mapeando Handlers
Para mapear un buffalo.Handler
, tendrás que asociarlo a una ruta específica con un método HTTP.
Métodos HTTP soportados
Buffalo soporta los siguientes métodos HTTP:
app.GET("/your/path", buffalo.Handler)
app.POST("/your/path", buffalo.Handler)
app.PUT("/your/path", buffalo.Handler)
app.PATCH("/your/path", buffalo.Handler)
app.DELETE("/your/path", buffalo.Handler)
app.OPTIONS("/your/path", buffalo.Handler)
app.HEAD("/your/path", buffalo.Handler)
También puedes hacer coincidir todos los métodos HTTP utilizando ANY
.
Por defecto, Buffalo establece una ruta raíz dentro de la configuración de bufalo.App:
// actions/app.go
func App() *buffalo.App {
// ...
app.GET("/", HomeHandler)
// ...
}
La asociación de múltiples buffalo.Handlers
a los métodos HTTP se ve de la siguiente manera:
// actions/app.go
app.GET("/", HomeHandler)
app.GET("/some/path", SomeHandler)
app.POST("/another/path", func (c buffalo.Context) error {
// do some work
})
// etc...
Como puedes ver, puedes declarar buffalo.Handlers en la misma línea si quieres.
Sin embargo, para una mayor legibilidad, a menudo es mejor separar los handlers en varios archivos. Por ejemplo, si tienes muchos handlers gestionando cosas de usuarios, puedes agruparlos en un archivo users.go
en la carpeta actions
, por ejemplo.
Denominanción de Rutas
Por defecto, Buffalo nombrará las rutas por ti en la forma de <nombre de la ruta>Path
.
Por ejemplo: a.GET("/coke", CokeHandler)
dará como resultado una ruta llamada cokePath
.
a.GET("/coke", CokeHandler) // cokePath()
Estos nombres se convierten en el nombre de los helpers de ruta en tus plantillas.
<a href="<%= cokePath() %>">Coke</a>
Denominación de Rutas Personalizadas
Buffalo también proporciona una forma de establecer un nombre personalizado para tu ruta, El método buffalo.RouteInfo#Name
te permite establecer un nombre personalizado para los helpers de la ruta.
Para personalizar el nombre de tu ruta, sólo tienes que utilizar el método Name después de asignar el método HTTP.
app.GET("/coke", CokeHandler).Name("customCoke") // customCokePath()
Esta ruta se llama ahora customCokePath
y puedes referenciarla como tal en tus plantillas.
<a href="<%= customCokePath() %>">Coke</a>
Lista de Rutas
Puedes revisar todas tus rutas ejecutando buffalo routes
desde la línea de comandos.
$ buffalo routes
METHOD | HOST | PATH | ALIASES | NAME | HANDLER
------ | ---- | ---- | ------- | ---- | -------
GET | http://127.0.0.1:3000 | / | | rootPath | coke/actions.HomeHandler
GET | http://127.0.0.1:3000 | /some/path/ | | somePath | coke/actions.SomeHandler
POST | http://127.0.0.1:3000 | /another/path/ | | anotherPath | coke/actions.App.func1
GET | http://127.0.0.1:3000 | /coke/ | | customCokePath | coke/actions.CokeHandler
path
pe. /widgets/new -> newWidgetsPath
; si la ruta cambia, entonces el nombre del helper de la ruta también cambia.
Ejemplo:
Mapeando WidgetResource
en la ruta /widgets
:
app.Resource("/widgets", WidgetsResource{})
Obtendrás los siguientes nombres de rutas:
$ buffalo routes
METHOD | HOST | PATH | ALIASES | NAME | HANDLER
------ | ---- | ---- | ------- | ---- | -------
GET | http://127.0.0.1:3000 | / | | rootPath | coke/actions.HomeHandler
GET | http://127.0.0.1:3000 | /some/path/ | | somePath | coke/actions.SomeHandler
POST | http://127.0.0.1:3000 | /another/path/ | | anotherPath | coke/actions.App.func1
GET | http://127.0.0.1:3000 | /coke/ | | customCokePath | coke/actions.CokeHandler
GET | http://127.0.0.1:3000 | /widgets/ | | widgetsPath | coke/actions.WidgetResource.List
POST | http://127.0.0.1:3000 | /widgets/ | | widgetsPath | coke/actions.WidgetResource.Create
GET | http://127.0.0.1:3000 | /widgets/new/ | | newWidgetsPath | coke/actions.WidgetResource.New
GET | http://127.0.0.1:3000 | /widgets/{widget_id}/ | | widgetPath | coke/actions.WidgetResource.Show
PUT | http://127.0.0.1:3000 | /widgets/{widget_id}/ | | widgetPath | coke/actions.WidgetResource.Update
DELETE | http://127.0.0.1:3000 | /widgets/{widget_id}/ | | widgetPath | coke/actions.WidgetResource.Destroy
GET | http://127.0.0.1:3000 | /widgets/{widget_id}/edit/ | | editWidgetPath | coke/actions.WidgetResource.Edit
Pero, si cambias el nombre de la ruta a /fooz
:
app.Resource("/fooz", WidgetsResource{})
Los nombres de las rutas pasarán a llamarse:
$ buffalo routes
METHOD | HOST | PATH | ALIASES | NAME | HANDLER
------ | ---- | ---- | ------- | ---- | -------
GET | http://127.0.0.1:3000 | / | | rootPath | coke/actions.HomeHandler
GET | http://127.0.0.1:3000 | /some/path/ | | somePath | coke/actions.SomeHandler
POST | http://127.0.0.1:3000 | /another/path/ | | anotherPath | coke/actions.App.func1
GET | http://127.0.0.1:3000 | /coke/ | | customCokePath | coke/actions.CokeHandler
GET | http://127.0.0.1:3000 | /fooz/ | | foozPath | coke/actions.WidgetResource.List
POST | http://127.0.0.1:3000 | /fooz/ | | foozPath | coke/actions.WidgetResource.Create
GET | http://127.0.0.1:3000 | /fooz/new/ | | newFoozPath | coke/actions.WidgetResource.New
GET | http://127.0.0.1:3000 | /fooz/{widget_id}/ | | foozWidgetIDPath | coke/actions.WidgetResource.Show
PUT | http://127.0.0.1:3000 | /fooz/{widget_id}/ | | foozWidgetIDPath | coke/actions.WidgetResource.Update
DELETE | http://127.0.0.1:3000 | /fooz/{widget_id}/ | | foozWidgetIDPath | coke/actions.WidgetResource.Destroy
GET | http://127.0.0.1:3000 | /fooz/{widget_id}/edit/ | | editFoozWidgetIDPath | coke/actions.WidgetResource.Edit
Revisa Denominación de Rutas Personalizadas
para más detalles sobre cómo cambiar el nombre generado.
Uso de los Helpers de ruta en las plantillas
Los helpers de ruta se pueden utilizar directamente en las plantillas utilizando el nombre del helper:
<%= widgetsPath() %> // /widgets
Las rutas que requieren parámetros con nombre, deben ser alimentadas con un mapa de esos parámetros.
<%= editWidgetPath({widget_id: 1}) %> --> /widgets/1/edit
El Helper pathFor
El helper pathFor
recibe una interface{}
o un slice
de estas, y trata de convertirlo en una ruta URL de estilo /foos/{id}
Reglas:
- si es
string
se devuelve tal cual - si es
Pathable
, se devuelve el métodoToPath
. - si se trata de un
slice
o de unarray
, cada elemento se pasa por el helper y luego se unen - si es
struct
, el nombre de la estructura se utiliza el plural para el nombre - si es
Paramable
el métodoToParam
se utiliza para rellenar el campo{id}
- si es
struct.Slug
el slug se utiliza para rellenar el campo{id}
de la URL - si es
struct.ID
el ID se utiliza para rellenar el campo{id}
de la URL
// 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"
}
Uso de los Helpers de ruta en las acciones
Redireccionamiento con helpers de ruta
También se pueden utilizar nombres de ruta cuando se redirige a otra url.
func MyHandler(c buffalo.Context) error {
return c.Redirect(http.StatusSeeOther, "widgetsPath()")
// Or with parameters
return c.Redirect(http.StatusSeeOther, "widgetPath()", render.Data{"widget_id": "1"})
}
Encontrar/llamar a un Helper de ruta
La función buffalo.RouteList#Lookup
permite buscar una ruta por su nombre desde la aplicación. Con el valor RouteInfo
de la ruta dada se puede generar la ruta para la misma.
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))
}
Parámetros
Query string y otros parámetros están disponibles en el buffalo.Context
que se pasa al buffalo.Handler
.
a.GET("/users", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("name")))
})
Dado el ejemplo de código anterior, si hacemos una petición con GET /users?name=ringo
, la respuesta debería ser 200: ringo
.
Parámetros con nombre
Dado que Buffalo es el github.com/gorilla/mux bajo las cubiertas, significa que podemos acceder a algunas de las bondades que proporciona. En este caso, la capacidad de crear patrones de expresiones pseudo-regulares en la ruta mapeada que se convertirán en parámetros a los que se puede acceder desde un buffalo.Context
.
a.GET("/users/{name}", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("name")))
})
Dado el ejemplo de código anterior, si hacemos una petición con GET /users/ringo
, la respuesta debería ser 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")))
})
Puedes asignar rutas aparentemente similares, como /users/new
y /users/{name}
sin ningún problema. El router se asegurará de que lleguen al mismo lugar.
Expresiones regulares
github.com/gorilla/mux proporciona una forma de utilizar expresiones regulares, para que puedas pre-filtrar las queries:
a.GET("/articles/{id:[0-9]+}", func (c buffalo.Context) error {
return c.Render(200, r.String(c.Param("id")))
})
Grupos
Las aplicaciones de Buffalo permiten agrupar los end-points. Esto permite reunir funcionalidades comunes, como los middleware. Un gran ejemplo de esto sería un end-point de la API.
g := a.Group("/api/v1")
g.Use(APIAuthorizer)
g.GET("/users", func (c buffalo.Context) error {
// responds to GET /api/v1/users
})
Por defecto, un grupo heredará cualquier middleware de su aplicación padre.
a.Use(SomeMiddleware)
g := a.Group("/api/v1")
g.Use(APIAuthorizer)
En el ejemplo anterior el grupo /api/v1
utilizará tanto SomeMiddleware
como APIAuthorizer
. Ver middleware para más información sobre el uso, la omisión y la eliminación de middleware.
Hosts Virtuales
Las aplicaciones de Buffalo también admiten la agrupación de end-points por host. La función VirtualHost
crea un nuevo grupo que coincide con el dominio introducido. Esto es útil para crear grupos de end-points para diferentes dominios o subdominios.
app := buffalo.New(buffalo.Options{
Env: envy.Get("GO_ENV", "development"),
SessionName: "_coke_session",
})
subApp := app.VirtualHost("docs.domain.com")
subApp.GET("/", func (c buffalo.Context) error {
return c.Render(http.StatusOK, r.String("docs.domain.com Homepage"))
})
domainApp := app.VirtualHost("example.com")
domainApp.GET("/", func (c buffalo.Context) error {
return c.Render(http.StatusOK, r.String("example.com Homepage"))
})
app.GET("/", func (c buffalo.Context) error {
return c.Render(http.StatusOK, r.String("Main App Homepage"))
})
También se admiten las variables asignadas a los parámetros:
app.VirtualHost("{subdomain}.example.com")
app.VirtualHost("{subdomain:[a-z]+}.example.com")
Montaje de aplicaciones http.Handler
A veces, querrás reutilizar algunos componentes de otras aplicaciones. Usando el método Mount
, puedes vincular un http.Handler
estándar a una ruta, igual que harías con un handler de ruta normal.
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())
Dado que Buffalo App
implementa la interfaz http.Handler
, también puedes montar otra aplicación de Buffalo y construir aplicaciones modulares.