logo Buffalo slack logo
Recursos
Gestión de Peticiones

Recursos

A menudo las aplicaciones Web necesitan desarrollar end-points muy parecidos al modelo CRUD. Para reducir la complejidad que esto añade, Buffalo soporta el concepto de “Recurso”.

La interfaz github.com/gobuffalo/buffalo#Resource permite a Buffalo asignar rutas comunes y responder a solicitudes comunes.

Desde 0.14.1
type Resource interface {
	List(Context) error
	Show(Context) error
	Create(Context) error
	Update(Context) error
	Destroy(Context) error
}

La interfaz github.com/gobuffalo/buffalo#Resource se ha reducido en la versión v0.14.1. Los métodos New y Edit, que sirven a los formularios HTML para editar el recurso, son ahora opcionales.

Así es como se veía la interfaz antes:

type Resource interface {
	List(Context) error
	Show(Context) error
	New(Context) error
	Create(Context) error
	Edit(Context) error
	Update(Context) error
	Destroy(Context) error
}

Uso de Recursos

Luego de implementar los métodos necesarios de la interfaz github.com/gobuffalo/buffalo#Resource, el recurso puede ser mapeado en la aplicación utilizando el método github.com/gobuffalo/buffalo#App.Resource.

// action/users.go
type UsersResource struct{ }

func (u UsersResource) List(c buffalo.Context) error {
  // do work
}

func (u UsersResource) Show(c buffalo.Context) error {
  // do work
}

func (u UsersResource) Create(c buffalo.Context) error {
  // do work
}

func (u UsersResource) Update(c buffalo.Context) error {
  // do work
}

func (u UsersResource) Destroy(c buffalo.Context) error {
  // do work
}

Mapeando el Recurso en app.go:

// actions/app.go
app.Resource("/users", UsersResource{})

El código de ejemplo anterior sería el equivalente al siguente:

// actions/app.go
ur := UsersResource{}

app.GET("/users", ur.List)
app.GET("/users/{user_id}", ur.Show)
app.POST("/users", ur.Create)
app.PUT("/users/{user_id}", ur.Update)
app.DELETE("/users/{user_id}", ur.Destroy)

Produciría una tabla de rutas que luce similar a la siguiente:

$ buffalo routes

METHOD | HOST                  | PATH                    | ALIASES | NAME                 | HANDLER
------ | ----                  | ----                    | ------- | ----                 | -------
GET    | http://127.0.0.1:3000 | /users/                 |         | usersPath            | coke/actions.UsersResource.List
POST   | http://127.0.0.1:3000 | /users/                 |         | usersPath            | coke/actions.UsersResource.Create
GET    | http://127.0.0.1:3000 | /users/{user_id}/       |         | userPath             | coke/actions.UsersResource.Show
PUT    | http://127.0.0.1:3000 | /users/{user_id}/       |         | userPath             | coke/actions.UsersResource.Update
DELETE | http://127.0.0.1:3000 | /users/{user_id}/       |         | userPath             | coke/actions.UsersResource.Destroy

Métodos de Recursos Opcionales

Desde 0.14.1

En la version v0.14.1 el github.com/gobuffalo/buffalo#Resource se redujo con los siguientes métodos que son ahora opcionales:

New(Context) error
Edit(Context) error

Cuando se implementan los métodos New y Edit se añade lo siguiente a la tabla de enrutamiento:

METHOD | HOST                   | PATH                   | ALIASES | NAME         | HANDLER
------ | ----                   | ----                   | ------- | ----         | -------
GET    | http://127.0.0.1:3000  | /users/new             |         | newUsersPath | coke/actions.UsersResource.New
GET    | http://127.0.0.1:3000  | /users/{user_id}/edit/ |         | editUserPath | coke/actions.UsersResource.Edit

Generando Recursos

El comando buffalo generate resource generará los modelos, las migraciones, el código Go y el HTML necesarios para el CRUD del recurso.

Cuando se ejecuta el generador en una aplicación API Buffalo generará código para cumplir con la interfaz github.com/gobuffalo/buffalo#Resource.

type Resource interface {
  List(Context) error
  Show(Context) error
  Create(Context) error
  Update(Context) error
  Destroy(Context) error
}

Cuando se ejecuta el generador en una aplicación web, Buffalo generará código para cumplir con la interfaz github.com/gobuffalo/buffalo#Resource, así como los métodos opcionales New y Edit

type Resource interface {
  List(Context) error
  Show(Context) error
  New(Context) error
  Create(Context) error
  Edit(Context) error
  Update(Context) error
  Destroy(Context) error
}

Ejemplo de Generación de Recursos

En este ejemplo Buffalo generará el código necesario para hacer un CRUD de un recurso llamado widget (Go: Widget) que tiene los siguientes atributos:

Model Attribute Go Type DB type Form Type
title Title string varchar text
description Description nulls.String varchar (nullable) textarea
$ buffalo generate resource widget title description:nulls.Text
package actions

import (
	"net/http"

	"coke/locales"
	"coke/models"
	"coke/public"

	"github.com/gobuffalo/buffalo"
	"github.com/gobuffalo/buffalo-pop/v3/pop/popmw"
	"github.com/gobuffalo/envy"
	csrf "github.com/gobuffalo/mw-csrf"
	forcessl "github.com/gobuffalo/mw-forcessl"
	i18n "github.com/gobuffalo/mw-i18n/v2"
	paramlogger "github.com/gobuffalo/mw-paramlogger"
	"github.com/unrolled/secure"
)

// ENV is used to help switch settings based on where the
// application is being run. Default is "development".
var ENV = envy.Get("GO_ENV", "development")

var (
	app *buffalo.App
	T   *i18n.Translator
)

// App is where all routes and middleware for buffalo
// should be defined. This is the nerve center of your
// application.
//
// Routing, middleware, groups, etc... are declared TOP -> DOWN.
// This means if you add a middleware to `app` *after* declaring a
// group, that group will NOT have that new middleware. The same
// is true of resource declarations as well.
//
// It also means that routes are checked in the order they are declared.
// `ServeFiles` is a CATCH-ALL route, so it should always be
// placed last in the route declarations, as it will prevent routes
// declared after it to never be called.
func App() *buffalo.App {
	if app == nil {
		app = buffalo.New(buffalo.Options{
			Env:         ENV,
			SessionName: "_coke_session",
		})

		// Automatically redirect to SSL
		app.Use(forceSSL())

		// Log request parameters (filters apply).
		app.Use(paramlogger.ParameterLogger)

		// Protect against CSRF attacks. https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
		// Remove to disable this.
		app.Use(csrf.New)

		// Wraps each request in a transaction.
		//   c.Value("tx").(*pop.Connection)
		// Remove to disable this.
		app.Use(popmw.Transaction(models.DB))
		// Setup and use translations:
		app.Use(translations())

		app.GET("/", HomeHandler)

		app.Resource("/widgets", WidgetsResource{})
		app.ServeFiles("/", http.FS(public.FS())) // serve files from the public directory
	}

	return app
}

// translations will load locale files, set up the translator `actions.T`,
// and will return a middleware to use to load the correct locale for each
// request.
// for more information: https://gobuffalo.io/en/docs/localization
func translations() buffalo.MiddlewareFunc {
	var err error
	if T, err = i18n.New(locales.FS(), "en-US"); err != nil {
		app.Stop(err)
	}
	return T.Middleware()
}

// forceSSL will return a middleware that will redirect an incoming request
// if it is not HTTPS. "http://example.com" => "https://example.com".
// This middleware does **not** enable SSL. for your application. To do that
// we recommend using a proxy: https://gobuffalo.io/en/docs/proxy
// for more information: https://github.com/unrolled/secure/
func forceSSL() buffalo.MiddlewareFunc {
	return forcessl.Middleware(secure.Options{
		SSLRedirect:     ENV == "production",
		SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
	})
}
package actions

import (
	"fmt"
	"net/http"

	"github.com/gobuffalo/buffalo"
	"github.com/gobuffalo/pop/v6"
	"github.com/gobuffalo/x/responder"

	"coke/models"
)

// This file is generated by Buffalo. It offers a basic structure for
// adding, editing and deleting a page. If your model is more
// complex or you need more than the basic implementation you need to
// edit this file.

// Following naming logic is implemented in Buffalo:
// Model: Singular (Widget)
// DB Table: Plural (widgets)
// Resource: Plural (Widgets)
// Path: Plural (/widgets)
// View Template Folder: Plural (/templates/widgets/)

// WidgetsResource is the resource for the Widget model
type WidgetsResource struct {
	buffalo.Resource
}

// List gets all Widgets. This function is mapped to the path
// GET /widgets
func (v WidgetsResource) List(c buffalo.Context) error {
	// Get the DB connection from the context
	tx, ok := c.Value("tx").(*pop.Connection)
	if !ok {
		return fmt.Errorf("no transaction found")
	}

	widgets := &models.Widgets{}

	// Paginate results. Params "page" and "per_page" control pagination.
	// Default values are "page=1" and "per_page=20".
	q := tx.PaginateFromParams(c.Params())

	// Retrieve all Widgets from the DB
	if err := q.All(widgets); err != nil {
		return err
	}

	return responder.Wants("html", func(c buffalo.Context) error {
		// Add the paginator to the context so it can be used in the template.
		c.Set("pagination", q.Paginator)

		c.Set("widgets", widgets)
		return c.Render(http.StatusOK, r.HTML("widgets/index.plush.html"))
	}).Wants("json", func(c buffalo.Context) error {
		return c.Render(200, r.JSON(widgets))
	}).Wants("xml", func(c buffalo.Context) error {
		return c.Render(200, r.XML(widgets))
	}).Respond(c)
}

// Show gets the data for one Widget. This function is mapped to
// the path GET /widgets/{widget_id}
func (v WidgetsResource) Show(c buffalo.Context) error {
	// Get the DB connection from the context
	tx, ok := c.Value("tx").(*pop.Connection)
	if !ok {
		return fmt.Errorf("no transaction found")
	}

	// Allocate an empty Widget
	widget := &models.Widget{}

	// To find the Widget the parameter widget_id is used.
	if err := tx.Find(widget, c.Param("widget_id")); err != nil {
		return c.Error(http.StatusNotFound, err)
	}

	return responder.Wants("html", func(c buffalo.Context) error {
		c.Set("widget", widget)

		return c.Render(http.StatusOK, r.HTML("widgets/show.plush.html"))
	}).Wants("json", func(c buffalo.Context) error {
		return c.Render(200, r.JSON(widget))
	}).Wants("xml", func(c buffalo.Context) error {
		return c.Render(200, r.XML(widget))
	}).Respond(c)
}

// New renders the form for creating a new Widget.
// This function is mapped to the path GET /widgets/new
func (v WidgetsResource) New(c buffalo.Context) error {
	c.Set("widget", &models.Widget{})

	return c.Render(http.StatusOK, r.HTML("widgets/new.plush.html"))
}

// Create adds a Widget to the DB. This function is mapped to the
// path POST /widgets
func (v WidgetsResource) Create(c buffalo.Context) error {
	// Allocate an empty Widget
	widget := &models.Widget{}

	// Bind widget to the html form elements
	if err := c.Bind(widget); err != nil {
		return err
	}

	// Get the DB connection from the context
	tx, ok := c.Value("tx").(*pop.Connection)
	if !ok {
		return fmt.Errorf("no transaction found")
	}

	// Validate the data from the html form
	verrs, err := tx.ValidateAndCreate(widget)
	if err != nil {
		return err
	}

	if verrs.HasAny() {
		return responder.Wants("html", func(c buffalo.Context) error {
			// Make the errors available inside the html template
			c.Set("errors", verrs)

			// Render again the new.html template that the user can
			// correct the input.
			c.Set("widget", widget)

			return c.Render(http.StatusUnprocessableEntity, r.HTML("widgets/new.plush.html"))
		}).Wants("json", func(c buffalo.Context) error {
			return c.Render(http.StatusUnprocessableEntity, r.JSON(verrs))
		}).Wants("xml", func(c buffalo.Context) error {
			return c.Render(http.StatusUnprocessableEntity, r.XML(verrs))
		}).Respond(c)
	}

	return responder.Wants("html", func(c buffalo.Context) error {
		// If there are no errors set a success message
		c.Flash().Add("success", T.Translate(c, "widget.created.success"))

		// and redirect to the show page
		return c.Redirect(http.StatusSeeOther, "/widgets/%v", widget.ID)
	}).Wants("json", func(c buffalo.Context) error {
		return c.Render(http.StatusCreated, r.JSON(widget))
	}).Wants("xml", func(c buffalo.Context) error {
		return c.Render(http.StatusCreated, r.XML(widget))
	}).Respond(c)
}

// Edit renders a edit form for a Widget. This function is
// mapped to the path GET /widgets/{widget_id}/edit
func (v WidgetsResource) Edit(c buffalo.Context) error {
	// Get the DB connection from the context
	tx, ok := c.Value("tx").(*pop.Connection)
	if !ok {
		return fmt.Errorf("no transaction found")
	}

	// Allocate an empty Widget
	widget := &models.Widget{}

	if err := tx.Find(widget, c.Param("widget_id")); err != nil {
		return c.Error(http.StatusNotFound, err)
	}

	c.Set("widget", widget)
	return c.Render(http.StatusOK, r.HTML("widgets/edit.plush.html"))
}

// Update changes a Widget in the DB. This function is mapped to
// the path PUT /widgets/{widget_id}
func (v WidgetsResource) Update(c buffalo.Context) error {
	// Get the DB connection from the context
	tx, ok := c.Value("tx").(*pop.Connection)
	if !ok {
		return fmt.Errorf("no transaction found")
	}

	// Allocate an empty Widget
	widget := &models.Widget{}

	if err := tx.Find(widget, c.Param("widget_id")); err != nil {
		return c.Error(http.StatusNotFound, err)
	}

	// Bind Widget to the html form elements
	if err := c.Bind(widget); err != nil {
		return err
	}

	verrs, err := tx.ValidateAndUpdate(widget)
	if err != nil {
		return err
	}

	if verrs.HasAny() {
		return responder.Wants("html", func(c buffalo.Context) error {
			// Make the errors available inside the html template
			c.Set("errors", verrs)

			// Render again the edit.html template that the user can
			// correct the input.
			c.Set("widget", widget)

			return c.Render(http.StatusUnprocessableEntity, r.HTML("widgets/edit.plush.html"))
		}).Wants("json", func(c buffalo.Context) error {
			return c.Render(http.StatusUnprocessableEntity, r.JSON(verrs))
		}).Wants("xml", func(c buffalo.Context) error {
			return c.Render(http.StatusUnprocessableEntity, r.XML(verrs))
		}).Respond(c)
	}

	return responder.Wants("html", func(c buffalo.Context) error {
		// If there are no errors set a success message
		c.Flash().Add("success", T.Translate(c, "widget.updated.success"))

		// and redirect to the show page
		return c.Redirect(http.StatusSeeOther, "/widgets/%v", widget.ID)
	}).Wants("json", func(c buffalo.Context) error {
		return c.Render(http.StatusOK, r.JSON(widget))
	}).Wants("xml", func(c buffalo.Context) error {
		return c.Render(http.StatusOK, r.XML(widget))
	}).Respond(c)
}

// Destroy deletes a Widget from the DB. This function is mapped
// to the path DELETE /widgets/{widget_id}
func (v WidgetsResource) Destroy(c buffalo.Context) error {
	// Get the DB connection from the context
	tx, ok := c.Value("tx").(*pop.Connection)
	if !ok {
		return fmt.Errorf("no transaction found")
	}

	// Allocate an empty Widget
	widget := &models.Widget{}

	// To find the Widget the parameter widget_id is used.
	if err := tx.Find(widget, c.Param("widget_id")); err != nil {
		return c.Error(http.StatusNotFound, err)
	}

	if err := tx.Destroy(widget); err != nil {
		return err
	}

	return responder.Wants("html", func(c buffalo.Context) error {
		// If there are no errors set a flash message
		c.Flash().Add("success", T.Translate(c, "widget.destroyed.success"))

		// Redirect to the index page
		return c.Redirect(http.StatusSeeOther, "/widgets")
	}).Wants("json", func(c buffalo.Context) error {
		return c.Render(http.StatusOK, r.JSON(widget))
	}).Wants("xml", func(c buffalo.Context) error {
		return c.Render(http.StatusOK, r.XML(widget))
	}).Respond(c)
}
package actions

func (as *ActionSuite) Test_WidgetsResource_List() {
	as.Fail("Not Implemented!")
}

func (as *ActionSuite) Test_WidgetsResource_Show() {
	as.Fail("Not Implemented!")
}

func (as *ActionSuite) Test_WidgetsResource_Create() {
	as.Fail("Not Implemented!")
}

func (as *ActionSuite) Test_WidgetsResource_Update() {
	as.Fail("Not Implemented!")
}

func (as *ActionSuite) Test_WidgetsResource_Destroy() {
	as.Fail("Not Implemented!")
}

func (as *ActionSuite) Test_WidgetsResource_New() {
	as.Fail("Not Implemented!")
}

func (as *ActionSuite) Test_WidgetsResource_Edit() {
	as.Fail("Not Implemented!")
}
- id: "widget.created.success"
  translation: "Widget was successfully created."
- id: "widget.updated.success"
  translation: "Widget was successfully updated."
- id: "widget.destroyed.success"
  translation: "Widget was successfully destroyed."
create_table("widgets") {
	t.Column("id", "uuid", {primary: true})
	t.Column("title", "string", {})
	t.Column("description", "text", {null: true})
	t.Timestamps()
}
drop_table("widgets")
package models

import (
	"encoding/json"
	"time"

	"github.com/gobuffalo/nulls"
	"github.com/gobuffalo/pop/v6"
	"github.com/gobuffalo/validate/v3"
	"github.com/gobuffalo/validate/v3/validators"
	"github.com/gofrs/uuid"
)

// Widget is used by pop to map your widgets database table to your go code.
type Widget struct {
	ID          uuid.UUID    `json:"id" db:"id"`
	Title       string       `json:"title" db:"title"`
	Description nulls.String `json:"description" db:"description"`
	CreatedAt   time.Time    `json:"created_at" db:"created_at"`
	UpdatedAt   time.Time    `json:"updated_at" db:"updated_at"`
}

// String is not required by pop and may be deleted
func (w Widget) String() string {
	jw, _ := json.Marshal(w)
	return string(jw)
}

// Widgets is not required by pop and may be deleted
type Widgets []Widget

// String is not required by pop and may be deleted
func (w Widgets) String() string {
	jw, _ := json.Marshal(w)
	return string(jw)
}

// Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.
// This method is not required and may be deleted.
func (w *Widget) Validate(tx *pop.Connection) (*validate.Errors, error) {
	return validate.Validate(
		&validators.StringIsPresent{Field: w.Title, Name: "Title"},
	), nil
}

// ValidateCreate gets run every time you call "pop.ValidateAndCreate" method.
// This method is not required and may be deleted.
func (w *Widget) ValidateCreate(tx *pop.Connection) (*validate.Errors, error) {
	return validate.NewErrors(), nil
}

// ValidateUpdate gets run every time you call "pop.ValidateAndUpdate" method.
// This method is not required and may be deleted.
func (w *Widget) ValidateUpdate(tx *pop.Connection) (*validate.Errors, error) {
	return validate.NewErrors(), nil
}
package models

func (ms *ModelSuite) Test_Widget() {
	ms.Fail("This test needs to be implemented!")
}
<%= f.InputTag("Title") %>
<%= f.TextAreaTag("Description", {rows: 10}) %>
<button class="btn btn-success" role="submit">Save</button>
<div class="py-4 mb-2">
  <h3 class="d-inline-block">Edit Widget</h3>
</div>

<%= formFor(widget, {action: widgetPath({ widget_id: widget.ID }), method: "PUT"}) { %>
  <%= partial("widgets/form.html") %>
  <%= linkTo(widgetPath({ widget_id: widget.ID }), {class: "btn btn-warning", "data-confirm": "Are you sure?", body: "Cancel"}) %>
<% } %>
<div class="py-4 mb-2">
  <h3 class="d-inline-block">Widgets</h3>
  <div class="float-end">
    <%= linkTo(newWidgetsPath(), {class: "btn btn-primary"}) { %>
      Create New Widget
    <% } %>
  </div>
</div>

<table class="table table-hover table-bordered">
  <thead class="thead-light">
    <th>Title</th>
    <th>&nbsp;</th>
  </thead>
  <tbody>
    <%= for (widget) in widgets { %>
      <tr>
        <td class="align-middle"><%= widget.Title %></td>
        <td>
          <div class="float-end">
            <%= linkTo(widgetPath({ widget_id: widget.ID }), {class: "btn btn-info", body: "View"}) %>
            <%= linkTo(editWidgetPath({ widget_id: widget.ID }), {class: "btn btn-warning", body: "Edit"}) %>
            <%= linkTo(widgetPath({ widget_id: widget.ID }), {class: "btn btn-danger", "data-method": "DELETE", "data-confirm": "Are you sure?", body: "Destroy"}) %>
          </div>
        </td>
      </tr>
    <% } %>
  </tbody>
</table>

<div class="text-center">
  <%= paginator(pagination) %>
</div>
<div class="py-4 mb-2">
  <h3 class="d-inline-block">New Widget</h3>
</div>

<%= formFor(widget, {action: widgetsPath(), method: "POST"}) { %>
  <%= partial("widgets/form.html") %>
  <%= linkTo(widgetsPath(), {class: "btn btn-warning", "data-confirm": "Are you sure?", body: "Cancel"}) %>
<% } %>
<div class="py-4 mb-2">
  <h3 class="d-inline-block">Widget Details</h3>

  <div class="float-end">
    <%= linkTo(widgetsPath(), {class: "btn btn-info"}) { %>
      Back to all Widgets
    <% } %>
    <%= linkTo(editWidgetPath({ widget_id: widget.ID }), {class: "btn btn-warning", body: "Edit"}) %>
    <%= linkTo(widgetPath({ widget_id: widget.ID }), {class: "btn btn-danger", "data-method": "DELETE", "data-confirm": "Are you sure?", body: "Destroy"}) %>
  </div>
</div>



<ul class="list-group mb-2 ">


  <li class="list-group-item pb-1">
    <label class="small d-block">Title</label>
    <p class="d-inline-block"><%= widget.Title %></p>
  </li>



  <li class="list-group-item pb-1">
    <label class="small d-block">Description</label>
    <p class="d-inline-block"><%= widget.Description %></p>
  </li>


</ul>

Eliminando recursos

Puedes eliminar los archivos generados por este generador ejecutando:

$ buffalo destroy resource users

Este comando te preguntará qué archivos quiere eliminar, puede responder a cada una de las preguntas con y/n o puede pasar el flag -y al comando de la siguiente manera:

$ buffalo destroy resource users -y

O en forma abreviada:

$ buffalo d r users -y

Anidación de Recursos

Para simplificar la creación de jerarquías de recursos, Buffalo admite la anidación de recursos.

type WidgetsResource struct {
	buffalo.Resource
}

type ImagesResource struct {
  buffalo.Resource
}

w := app.Resource("/widgets", WidgetsResource{})
w.Resource("/images", ImagesResource{})

Esto da como resultado las siguientes 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 | /widgets/                                    |         | widgetsPath         | coke/actions.WidgetsResource.List
POST   | http://127.0.0.1:3000 | /widgets/                                    |         | widgetsPath         | coke/actions.WidgetsResource.Create
GET    | http://127.0.0.1:3000 | /widgets/new/                                |         | newWidgetsPath      | coke/actions.WidgetsResource.New
GET    | http://127.0.0.1:3000 | /widgets/{widget_id}/                        |         | widgetPath          | coke/actions.WidgetsResource.Show
PUT    | http://127.0.0.1:3000 | /widgets/{widget_id}/                        |         | widgetPath          | coke/actions.WidgetsResource.Update
DELETE | http://127.0.0.1:3000 | /widgets/{widget_id}/                        |         | widgetPath          | coke/actions.WidgetsResource.Destroy
GET    | http://127.0.0.1:3000 | /widgets/{widget_id}/edit/                   |         | editWidgetPath      | coke/actions.WidgetsResource.Edit
GET    | http://127.0.0.1:3000 | /widgets/{widget_id}/images/                 |         | widgetImagesPath    | coke/actions.ImagesResource.List
POST   | http://127.0.0.1:3000 | /widgets/{widget_id}/images/                 |         | widgetImagesPath    | coke/actions.ImagesResource.Create
GET    | http://127.0.0.1:3000 | /widgets/{widget_id}/images/new/             |         | newWidgetImagesPath | coke/actions.ImagesResource.New
GET    | http://127.0.0.1:3000 | /widgets/{widget_id}/images/{image_id}/      |         | widgetImagePath     | coke/actions.ImagesResource.Show
PUT    | http://127.0.0.1:3000 | /widgets/{widget_id}/images/{image_id}/      |         | widgetImagePath     | coke/actions.ImagesResource.Update
DELETE | http://127.0.0.1:3000 | /widgets/{widget_id}/images/{image_id}/      |         | widgetImagePath     | coke/actions.ImagesResource.Destroy
GET    | http://127.0.0.1:3000 | /widgets/{widget_id}/images/{image_id}/edit/ |         | editWidgetImagePath | coke/actions.ImagesResource.Edit

buffalo.BaseResource

Cuando se genera un recurso, éste tiene incorporado el buffalo.BaseResource.

type Widget struct {
  buffalo.BaseResource
}

El buffalo.BaseResource tiene implementaciones básicas para todos los métodos requeridos por buffalo.Resource. Todos estos métodos retornan un 404 por defecto.

// Edit default implementation. Returns a 404
func (v BaseResource) Edit(c Context) error {
  return c.Error(http.StatusNotFound, errors.New("resource not implemented"))
}

Video de presentación

Contenido relacionado

  • Acciones - Más información sobre las acciones de Buffalo.

Siguientes pasos

  • Contexto - Más información sobre el contexto de Buffalo.