Métodos estándar

En este capítulo se define el concepto de métodos estándares, 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 las 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> No disponible Lista de recursos*
Get GET <resource URL> No disponible 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 que muestran los métodos List, Get, Create y Update puede contener datos parciales si los métodos son compatibles con máscaras de campo de respuesta, que especifican un subconjunto de campos para mostrar. En algunos casos, la plataforma de API admite de forma nativa las máscaras de campo para todos los métodos.

**La respuesta que muestra un método Delete que no quita el recurso de forma inmediata (como actualizar una marca o crear una operación de borrado de larga duración) debe 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.

Enumerar

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

Por lo general, List se usa para buscar recursos. List es adecuada para datos de una sola colección con límite de tamaño que no está almacenada en caché. Para casos más amplios, debe usarse el método personalizado Search.

Un get por lotes (como un método que toma varios ID de recursos y muestra un objeto por cada uno de esos ID) debe implementarse como un método BatchGet personalizado, en lugar de un List. Sin embargo, si tienes un método List ya existente que proporciona la misma funcionalidad, puedes volver a usar el método List para este fin. Si usas 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 la 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 muestra 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 la 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 superior, 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, debe tener un método Create para cada tipo de recurso que se pueda 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 superior donde se creará el recurso nuevo.
  • 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 que los emisores seleccionen un ID asignado 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 debe fallar con el código de error ALREADY_EXISTS o usar otro nombre de recurso asignado por el servidor, y la documentación debe aclarar que el nombre del recurso creado puede ser diferente del que se pasó.

El método Create debe tomar un recurso de entrada para que, cuando se modifique el esquema de recursos, no sea necesario actualizar el esquema de solicitudes y el esquema de recursos. 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 poder mutarse con el método Update, excepto las propiedades que contienen el nombre o el superior del recurso. Cualquier funcionalidad para renombrar o mover un recurso no debe ocurrir en el método Update y, en su lugar, se manejará mediante un método personalizado.

Asignación HTTP:

  • El método Update estándar debería admitir la actualización parcial de recursos y usar 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 requiera una semántica de parches más avanzada, como adjuntar a un campo repetido, debe 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, ya que tiene problemas de retrocompatibilidad 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 debe fallar con un nombre de recurso inexistente. Sedebe usar 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 también debe proporcionar un método Create. La razón es que no queda claro cómo crear recursos si el método Update es la única manera 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 borra o programa la eliminación del recurso especificado. El método Delete debe mostrar 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 la API no debe declarar una cláusula body.
  • Si el método Delete quita el recurso de inmediato, debe mostrar una respuesta vacía.
  • Si el método Delete inicia una operación de larga duración, debe mostrarla.
  • Si el método Delete solo marca el recurso como borrado, debe mostrar el recurso actualizado.

Las llamadas al método Delete deben ser idempotentes en efecto, pero no es necesario que brinden la misma respuesta. Cualquier cantidad de solicitudes Delete debe dar como resultado que un recurso se borre (en algún momento), pero solo la primera solicitud debe dar como resultado un código de éxito. Las solicitudes posteriores deben dar como resultado una 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;
}