Métodos estándar

Este capítulo define el concepto de métodos estándar, que son List, Get, Create, Update y Delete. Los métodos estándar reducen la complejidad y aumentan la coherencia. Más del 70% de los métodos de API en el repositorio de API de Google son métodos estándar, lo que los hace mucho más fáciles de aprender y usar.

En la siguiente tabla, se describe cómo asignar métodos estándar a métodos HTTP:

Método estándar Asignación HTTP Cuerpo de la solicitud HTTP Cuerpo de la respuesta HTTP
List GET <collection URL> N/A Lista de recursos*
Get GET <resource URL> N/A Recurso*
Create POST <collection URL> Recurso Recurso*
Update PUT or PATCH <resource URL> Recurso Recurso*
Delete DELETE <resource URL> N/A google.protobuf.Empty**

*El recurso devuelto por List, Get, Create y Update métodos puede contener datos parciales si los métodos admiten máscaras de campo de respuesta, que especifican un subconjunto campos a devolver. En algunos casos, la plataforma de API admite de forma nativa las máscaras de campo para todos los métodos.

**La respuesta devuelta por un método Delete que no elimina inmediatamente el recurso (como actualizar un indicador o crear una operación de eliminación de larga duración) debería contener la operación de larga duración o el recurso modificado.

Un método estándar también puede mostrar una operación de larga duración para solicitudes que no se completan dentro del período de la única llamada a la API.

En las siguientes secciones, se describen cada uno de los métodos estándar en detalle. Los ejemplos muestran los métodos definidos en archivos .proto con anotaciones especiales para las asignaciones HTTP. Puedes encontrar muchos ejemplos que usan métodos estándar en el repositorio de API de Google.

List

El método List toma un nombre de colección y cero o más parámetros como entrada, y devuelve una lista de recursos que coinciden con la entrada.

List se usa comúnmente para buscar recursos. List es adecuado para datos de una sola colección que está limitada en tamaño y no almacenada en caché. Para casos más amplios, se debe utilizar el método personalizado Search.

Una obtención por lotes (como un método que toma múltiples ID de recursos y devuelve un objeto para cada una de esas ID) debe implementarse como un método personalizado BatchGet, en lugar de un List. Sin embargo, si tiene un método List ya existente que proporciona la misma funcionalidad, puede reutilizar el método List para este propósito. Si está utilizando un método BatchGet personalizado, debe asignarse a HTTP GET.

Patrones comunes aplicables: paginación, ordenamiento de resultados.

Convenciones de nomenclatura aplicables: campo de filtro, campo de resultados

Asignación HTTP:

  • El método List debe usar un verbo HTTP GET.
  • Los campos del mensaje de solicitud que reciben el nombre de la colección cuyos recursos se enumeran deben asignarse a la ruta de la URL. Si el nombre de la colección se asigna a la ruta de URL, el último segmento de la plantilla URL (el ID de colección) debe ser literal.
  • Todos los campos de mensaje de solicitud restantes se asignarán a los parámetros de consulta de URL.
  • No hay cuerpo de solicitud; la configuración de API no debe declarar una cláusula body.
  • El cuerpo de la respuesta debe contener una lista de recursos junto con metadatos opcionales.

Ejemplo:

// Lists books in a shelf.
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
  // List method maps to HTTP GET.
  option (google.api.http) = {
    // The `parent` captures the parent resource name, such as "shelves/shelf1".
    get: "/v1/{parent=shelves/*}/books"
  };
}

message ListBooksRequest {
  // The parent resource name, for example, "shelves/shelf1".
  string parent = 1;

  // The maximum number of items to return.
  int32 page_size = 2;

  // The next_page_token value returned from a previous List request, if any.
  string page_token = 3;
}

message ListBooksResponse {
  // The field name should match the noun "books" in the method name.  There
  // will be a maximum number of items returned based on the page_size field
  // in the request.
  repeated Book books = 1;

  // Token to retrieve the next page of results, or empty if there are no
  // more results in the list.
  string next_page_token = 2;
}

Get

El método Get toma un nombre de recurso, cero o más parámetros y devuelve el recurso especificado.

Asignación HTTP:

  • El método Get debe usar un verbo HTTP GET.
  • Los campos de mensaje de solicitud que reciben el nombre del recurso deben asignarse a la ruta de URL.
  • Todos los campos de mensaje de solicitud restantes se asignarán a los parámetros de consulta de URL.
  • No hay cuerpo de solicitud; la configuración de API no debe declarar una cláusula body.
  • El recurso mostrado se asignará a todo el cuerpo de la respuesta.

Ejemplo:

// Gets a book.
rpc GetBook(GetBookRequest) returns (Book) {
  // Get maps to HTTP GET. Resource name is mapped to the URL. No body.
  option (google.api.http) = {
    // Note the URL template variable which captures the multi-segment resource
    // name of the requested book, such as "shelves/shelf1/books/book2"
    get: "/v1/{name=shelves/*/books/*}"
  };
}

message GetBookRequest {
  // The field will contain name of the resource requested, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}

Crear

El método Create toma un nombre de recurso primario, un recurso y cero o más parámetros. Crea un nuevo recurso bajo el superior especificado y lo muestra.

Si una API admite la creación de recursos, debería tener un método Create para cada tipo de recurso que se puede crear.

Asignación HTTP:

  • El método Create debe usar un verbo HTTP POST.
  • El mensaje de solicitud debe tener un campo parent que especifique el nombre del recurso principal donde se creará el recurso.
  • El campo de mensaje de solicitud que contiene el recurso debe asignarse al cuerpo de la solicitud HTTP. Si se usa la anotación google.api.http para el método Create, se debe usar el formulario body: "<resource_field>".
  • La solicitud puede contener un campo llamado <resource>_id para permitir a las personas que llaman seleccionar una identificación asignada por el cliente. Este campo puede estar dentro del recurso.
  • Todos los campos de mensaje de solicitud restantes se asignarán a los parámetros de consulta de URL.
  • El recurso mostrado se asignará a todo el cuerpo de la respuesta HTTP.

Si el método Create admite el nombre de recurso asignado por el cliente y el recurso ya existe, la solicitud debería fallar con el código de error ALREADY_EXISTS o utilizar un nombre de recurso asignado por el servidor diferente y la documentación debe quedar claro que el nombre del recurso creado puede ser diferente del que se pasó.

El método Create debe tomar un recurso de entrada, de modo que cuando el esquema del recurso cambia, no hay necesidad de actualizar tanto el esquema de solicitud como el esquema de recurso. Los campos de recursos que los clientes no pueden configurar deben documentarse como campos “Solo de salida”.

Ejemplo:

// Creates a book in a shelf.
rpc CreateBook(CreateBookRequest) returns (Book) {
  // Create maps to HTTP POST. URL path as the collection name.
  // HTTP request body contains the resource.
  option (google.api.http) = {
    // The `parent` captures the parent resource name, such as "shelves/1".
    post: "/v1/{parent=shelves/*}/books"
    body: "book"
  };
}

message CreateBookRequest {
  // The parent resource name where the book is to be created.
  string parent = 1;

  // The book id to use for this book.
  string book_id = 3;

  // The book resource to create.
  // The field name should match the Noun in the method name.
  Book book = 2;
}

rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
  option (google.api.http) = {
    post: "/v1/shelves"
    body: "shelf"
  };
}

message CreateShelfRequest {
  Shelf shelf = 1;
}

Actualizar

El método Update toma un mensaje de solicitud que contiene un recurso y cero o más parámetros. Actualiza el recurso especificado y sus propiedades, y luego lo muestra.

Las propiedades de recursos mutables deben ser mutables mediante el método Update, excepto las propiedades que contienen el nombre del recurso o el padre. Cualquier funcionalidad para renombrar o mover un recurso no debe suceder en el método Update y en su lugar deberá ser manejado por un método personalizado.

Asignación HTTP:

  • El método estándar Update debería admitir la actualización parcial de recursos y utilizar el verbo HTTP PATCH con un campo FieldMask llamado update_mask. Los campos de salida que proporcione el cliente como entradas deben ignorarse.
  • Un método Update que requiere una semántica de parches más avanzada, como agregar un campo repetido, debería estar disponible mediante un método personalizado.
  • Si el método Update solo admite la actualización completa de recursos, debe usar el verbo HTTP PUT. Sin embargo, no se recomienda la actualización completa porque tiene problemas de compatibilidad con versiones anteriores cuando se agregan nuevos campos de recursos.
  • El campo de mensaje que recibe el nombre del recurso debe asignarse a la ruta de URL. El campo puede encontrarse en el propio mensaje de recursos.
  • El campo de mensaje de solicitud que contiene el recurso debe asignarse al cuerpo de la solicitud.
  • Todos los campos de mensaje de solicitud restantes deben asignarse a los parámetros de consulta de URL.
  • El mensaje de respuesta debe ser el propio recurso actualizado.

Si la API acepta nombres de recursos asignados por el cliente, el servidor puede permitir que el cliente especifique un nombre de recurso inexistente y cree un nuevo recurso. De lo contrario, el método Update debería fallar con un nombre de recurso inexistente. Debe usarse el código de error NOT_FOUND si es la única condición de error.

Una API con un método Update que admita la creación de recursos debería también proporcionar un método Create. La razón es que no está claro cómo crear recursos si el método Update es la única forma de hacerlo.

Ejemplo:

// Updates a book.
rpc UpdateBook(UpdateBookRequest) returns (Book) {
  // Update maps to HTTP PATCH. Resource name is mapped to a URL path.
  // Resource is contained in the HTTP request body.
  option (google.api.http) = {
    // Note the URL template variable which captures the resource name of the
    // book to update.
    patch: "/v1/{book.name=shelves/*/books/*}"
    body: "book"
  };
}

message UpdateBookRequest {
  // The book resource which replaces the resource on the server.
  Book book = 1;

  // The update mask applies to the resource. For the `FieldMask` definition,
  // see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
  FieldMask update_mask = 2;
}

Borrar

El método Delete toma un nombre de recurso y cero o más parámetros, y elimina o programa la eliminación del recurso especificado. El método Delete debería devolver google.protobuf.Empty.

Una API no debe basarse en la información que muestra un método Delete, ya que no se puede invocar de manera reiterada.

Asignación HTTP:

  • El método Delete debe usar un verbo HTTP DELETE.
  • Los campos de mensaje de solicitud que reciben el nombre del recurso deben asignarse a la ruta de URL.
  • Todos los campos de mensaje de solicitud restantes se asignarán a los parámetros de consulta de URL.
  • No hay cuerpo de solicitud; la configuración de API no debe declarar una cláusula body.
  • Si el método Delete elimina inmediatamente el recurso, debería devolver una respuesta vacía.
  • Si el método Delete inicia una operación de larga duración, debería devolver la operación de larga duración.
  • Si el método Delete solo marca el recurso como eliminado, debería devolver el recurso actualizado.

Las llamadas al método Delete deben tener efecto idempotente, pero no es necesario que den la misma respuesta. Cualquier cantidad de Delete solicitudes debería resultar en la eliminación (eventual) de un recurso, pero solo la primera solicitud debería generar un código de éxito. Las solicitudes posteriores deben dar como resultado un google.rpc.Code.NOT_FOUND.

Ejemplo:

// Deletes a book.
rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {
  // Delete maps to HTTP DELETE. Resource name maps to the URL path.
  // There is no request body.
  option (google.api.http) = {
    // Note the URL template variable capturing the multi-segment name of the
    // book resource to be deleted, such as "shelves/shelf1/books/book2"
    delete: "/v1/{name=shelves/*/books/*}"
  };
}

message DeleteBookRequest {
  // The resource name of the book to be deleted, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}