logo Buffalo slack logo
Enrutamiento
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.

Consulte Contexto para entender la interfaz 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
IMPORTANTE: Dado que los nombres de los helpers de las rutas se calculan usando el 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étodo ToPath.
  • si se trata de un slice o de un array, 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étodo ToParam 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

Desde 0.13.0-beta.1

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

Desde 0.18.2

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

Desde 0.9.4

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.