Diseñe la API de consulta RESTful con una larga lista de parámetros de consulta [cerrado]

153

Necesito diseñar una API de consulta RESTful, que devuelva un conjunto de objetos basados ​​en unos pocos filtros. El método HTTP habitual para esto es GET. El único problema es que puede tener al menos una docena de filtros, y si los pasamos todos como parámetros de consulta, la URL puede ser bastante larga (lo suficiente como para ser bloqueada por algún firewall).

Reducir el número de parámetros no es una opción.

Una alternativa que se me ocurre es utilizar el método POST en el URI y enviar los filtros como parte del cuerpo POST. ¿Es esto en contra de ser RESTfull (Hacer una llamada POST para consultar datos).

¿Alguien tiene alguna sugerencia de diseño mejor?

missionE46
fuente
2
¿Usa nombres de parámetros cortos (1-char, etc.)?
Madbreaks
2
Puede que no sea realmente RESTful, pero creo que debes ser práctico cuando se trata de GETs y POSTs. Si tiene tantas variables para enviar y no puede reducirlas, las PUBLICARÍA. No me gusta sobrecargar la URL, pero solo soy yo.
Doug Dawson el
Gracias. Aunque esta pregunta está cerrada, es EXACTAMENTE la pregunta a la que necesitaba una respuesta. Me alegra que lo hayas preguntado.
Casey Crookston

Respuestas:

142

Recuerde que con una API REST, todo se trata de su punto de vista.

Los dos conceptos clave en una API REST son los puntos finales y los recursos (entidades). En términos generales, un punto final devuelve recursos a través de GET o acepta recursos a través de POST y PUT y así sucesivamente (o una combinación de los anteriores).

Se acepta que con POST, los datos que envíe pueden o no resultar en la creación de un nuevo recurso y sus puntos finales asociados, que probablemente no "vivan" bajo la URL POSTed. En otras palabras, cuando PUBLICA, envía datos a algún lugar para su manejo. El punto final POST no es donde normalmente se puede encontrar el recurso.

Citando de RFC 2616 (con partes irrelevantes omitidas y partes relevantes resaltadas):

9.5 POST

El método POST se utiliza para solicitar que el servidor de origen acepte la entidad incluida en la solicitud como un nuevo subordinado del recurso identificado por el URI de solicitud en la línea de solicitud. POST está diseñado para permitir un método uniforme para cubrir las siguientes funciones:

  • ...
  • Proporcionar un bloque de datos, como el resultado de enviar un formulario, a un proceso de manejo de datos;
  • ...

...

La acción realizada por el método POST podría no dar como resultado un recurso que pueda ser identificado por un URI . En este caso, 200 (OK) o 204 (Sin contenido) es el estado de respuesta apropiado, dependiendo de si la respuesta incluye o no una entidad que describa el resultado .

Si se ha creado un recurso en el servidor de origen, la respuesta DEBERÍA ser 201 (Creado) ...

Nos hemos acostumbrado a los puntos finales y los recursos que representan 'cosas' o 'datos', ya sea un usuario, un mensaje, un libro, cualquiera que sea el dominio del problema. Sin embargo, un punto final también puede exponer un recurso diferente, por ejemplo, resultados de búsqueda.

Considere el siguiente ejemplo:

GET    /books?author=AUTHOR
POST   /books
PUT    /books/ID
DELETE /books/ID

Este es un CRUDO DE RESTO típico. Sin embargo, ¿qué pasa si agregamos:

POST /books/search

    {
        "keywords": "...",
        "yearRange": {"from": 1945, "to": 2003},
        "genre": "..."
    }

No hay nada RESTful sobre este punto final. Acepta datos (entidad) en la forma del cuerpo de la solicitud. Esos datos son los criterios de búsqueda , un DTO como cualquier otro. Este punto final produce un recurso (entidad) en respuesta a la solicitud: Resultados de búsqueda . El recurso de resultados de búsqueda es temporal, se sirve inmediatamente al cliente, sin redireccionamiento y sin exponerse desde alguna otra URL canónica.

Todavía es REST, excepto que las entidades no son libros: la entidad de solicitud es el criterio de búsqueda de libros y la entidad de respuesta son los resultados de búsqueda de libros.

Amir Abiri
fuente
¿Podría sugerirnos algunas clases de convenciones de nombres para el DTO?
Kwadz
Personalmente iría con BooksSearchCriteriaDTOy BooksSearchResultsDTO.
Amir Abiri
¿Cuál sería el mejor código de respuesta HTTP para este caso de POST / books / search? 201 todavía se aplica?
L. Holanda
9
201 es lo contrario: implica que se ha creado un recurso. Un recurso que se espera que tenga su propio URI único en alguna parte. 201 es adecuado cuando POSTse usa para la parte C de CRUD. Iría con el viejo y simple 200, opcionalmente con 204 para los resultados de búsqueda vacíos.
Amir Abiri
@AmirAbiri muchas gracias.
mohamed-mhiri
84

Mucha gente ha aceptado la práctica de que un GET con una cadena de consulta demasiado larga o demasiado compleja (por ejemplo, las cadenas de consulta no manejan datos anidados fácilmente) puede enviarse como una POST, con los datos complejos / largos representados en el cuerpo de la solicitud.

Busque la especificación POST en la especificación HTTP. Es increíblemente amplio. (Si quieres navegar en un acorazado a través de una escapatoria en REST ... usa POST).

Pierde algunos de los beneficios de la semántica GET ... como reintentos automáticos porque GET es idempotente, pero si puede vivir con eso, podría ser más fácil aceptar el procesamiento de consultas realmente largas o complicadas con POST.

(jajaja digresión larga ... Hace poco descubrí que por la especificación HTTP, GET puede contener un cuerpo de documento. Hay una sección que dice, parafraseando, "Cualquier solicitud puede tener un cuerpo de documento excepto los enumerados en esta sección" ... y la sección a la que hace referencia no enumera ninguno. Busqué y encontré un hilo donde los autores de HTTP hablaban de eso, y fue intencional, para que los enrutadores y demás no tuvieran que diferenciar entre diferentes mensajes. Sin embargo, en practique muchas piezas de infraestructura y deje caer el cuerpo de un GET. Entonces podría hacerlo con filtros representados en el cuerpo, como POST, pero estaría tirando los dados).

Robar
fuente
11
Consulte también esta pregunta para obtener más información sobre HTTP GET con body.
RickyA
6

En pocas palabras: haga una POST pero anule el método HTTP utilizando el encabezado X-HTTP-Method-Override .

Solicitud real

POST / libros

Cuerpo de la entidad

{"title": "Ipsum", "year": 2017}

Encabezados

X-HTTP-Method-Override: GET

En el lado del servidor, verifique si el encabezado X-HTTP-Method-Override existe y luego tome su valor como el método para construir la ruta al punto final final en el back-end. Además, tome el cuerpo de la entidad como la cadena de consulta. Desde el punto de vista del backend, la solicitud se convirtió en un simple GET.

De esta manera, mantiene el diseño en armonía con los principios REST.

Editar: Sé que esta solución originalmente estaba destinada a resolver el problema del verbo PATCH en algunos navegadores y servidores, pero también funciona para mí con el verbo GET en el caso de una URL muy larga, que es el problema descrito en la pregunta.

Delmo
fuente
2
IETF en desuso X- encabezados HTTP con prefijo: tools.ietf.org/html/rfc6648
enero
@jannis El RFC que enlaza permanece 1.4. no hace ninguna recomendación sobre la X-eliminación existente y 1.5. no anula las especificaciones existentes. ... X-OMI permanecerá aquí.
Jan Molnar
-3

Si está desarrollando en Java y JAX-RS, le recomiendo que use @QueryParam con @GET

Tenía la misma pregunta cuando necesitaba revisar una lista.

Ver ejemplo:

import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/poc")
public class UserService {

    @GET
    @Path("/test/")
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(@QueryParam("code") final List<Integer> code) {
                Integer int0 = codigo.get(0);
                Integer int1 = codigo.get(1);

        return Response.ok(new JSONObject().put("int01", int0)).build();
    }
}

Patrón URI: "poc / test? Code = 1 & code = 2 & code = 3

@QueryParam convertirá el parámetro de consulta "orderBy = age & orderBy = name" en java.util.List automáticamente.

acacio.martins
fuente
Sería mejor si explicas tu ejemplo. ¿En qué lenguaje de programación está escrito?
Aleks Andreev
Hola @AleksAndreev. Gracias por tu opinión. Se puso mejor? TKS
acacio.martins
Esta pregunta es sobre el diseño del servicio RESTful, no sobre la implementación. Esta respuesta no responde la pregunta.
Heretic Monkey
@ user1331413 En mi humilde opinión, sí, ahora es mejor. Gracias por su esfuerzo. Sin embargo, como dijo Mike McCaughan, la pregunta es sobre el concepto REST, en lugar de la implementación
Aleks Andreev