API REST: ¿por qué usar PUT DELETE POST GET?

155

Entonces, estaba mirando algunos artículos sobre cómo crear API REST. Y algunos de ellos sugieren usar todo tipo de solicitudes HTTP: como PUT DELETE POST GET. Creamos, por ejemplo, index.php y escribimos API de esta manera:

$method = $_SERVER['REQUEST_METHOD'];
$request = split("/", substr(@$_SERVER['PATH_INFO'], 1));

switch ($method) {
  case 'PUT':
    ....some put action.... 
    break;
  case 'POST':
    ....some post action.... 
    break;
  case 'GET':
    ....some get action.... 
    break;
  case 'DELETE':
    ....some delete action.... 
    break;
}

De acuerdo, no sé mucho sobre los servicios web (todavía). Pero, ¿no sería más fácil aceptar el objeto JSON a través de regular POSTo GET(que contendría el nombre del método y todos los parámetros) y luego responder también en JSON. Podemos fácilmente serializar / deserializar a través de PHP de json_encode()y json_decode()y hacer lo que queramos con esos datos sin tener que lidiar con diferentes métodos de petición HTTP.

¿Me estoy perdiendo de algo?

ACTUALIZACIÓN 1:

Ok, después de explorar varias API y aprender mucho sobre XML-RPC , JSON-RPC , SOAP , REST , llegué a la conclusión de que este tipo de API es sólido. En realidad, el intercambio de pila está utilizando este enfoque en sus sitios y creo que estas personas saben lo que están haciendo Stack Exchange API .

Stann
fuente
44
¿Por qué forzar una carga JSON? ¿Qué pasa si no hay JSON, y es un viejo GET?
Mike DeSimone

Respuestas:

200

La idea de RE presentational S tate T ransfer no se trata de acceder a los datos de la manera más simple posible.

Sugirió usar solicitudes de publicación para acceder a JSON, que es una forma perfectamente válida de acceder / manipular datos.

REST es una metodología para el acceso significativo de datos. Cuando vea una solicitud en REST, debería aparecer inmediatamente lo que está sucediendo con los datos.

Por ejemplo:

GET: /cars/make/chevrolet

es probable que devuelva una lista de autos chevy. Una buena API REST podría incluso incorporar algunas opciones de salida en la cadena de consulta como ?output=jsono ?output=htmllo que permitiría al accesor decidir en qué formato debe codificarse la información.

Después de un poco de pensar acerca de cómo la escritura de datos razonablemente integrarse en una API REST, he concluido que la mejor manera de especificar el tipo de datos de forma explícita sería a través de la extensión del archivo ya existente, como .js, .json, .html, o .xml. Una extensión de archivo faltante tendría el formato predeterminado predeterminado (como JSON); una extensión de archivo que no sea compatible podría devolver un 501 Not Implementedcódigo de estado .

Otro ejemplo:

POST: /cars/
{ make:chevrolet, model:malibu, colors:[red, green, blue, grey] }

es probable que cree un nuevo chevy malibu en la base de datos con los colores asociados. Digo probablemente ya que la API REST no necesita estar directamente relacionada con la estructura de la base de datos. Es solo una interfaz de enmascaramiento para que los datos verdaderos estén protegidos (piense en ellos como accesores y mutadores para una estructura de base de datos).

Ahora necesitamos pasar al tema de la idempotencia . Por lo general, REST implementa CRUD sobre HTTP. HTTP utiliza GET, PUT, POSTy DELETEpara las solicitudes.

Una implementación muy simple de REST podría usar el siguiente mapeo CRUD:

Create -> Post
Read   -> Get
Update -> Put
Delete -> Delete

Hay un problema con esta implementación: la publicación se define como un método no idempotente. Esto significa que las llamadas posteriores del mismo método Post darán como resultado diferentes estados del servidor. Get, Put y Delete son idempotentes; lo que significa que llamarlos varias veces debería dar como resultado un estado de servidor idéntico.

Esto significa que una solicitud como:

Delete: /cars/oldest

en realidad podría implementarse como:

Post: /cars/oldest?action=delete

Mientras

Delete: /cars/id/123456

resultará en el mismo estado del servidor si lo llama una vez o si lo llama 1000 veces.

Una mejor manera de manejar la eliminación del oldestartículo sería solicitar:

Get: /cars/oldest

y use los IDdatos resultantes para hacer una deletesolicitud:

Delete: /cars/id/[oldest id]

Un problema con este método sería si /carsse agregara otro elemento entre cuando /oldestse solicitó y cuándo deletese emitió.

zzzzBov
fuente
3
@Andre es una combinación de varias razones: seguir las pautas de HTTP significa que (probablemente) tendrá menos problemas de compatibilidad con versiones anteriores cuando las cosas cambien; el uso de un formulario html a través de POST advertirá al usuario de múltiples envíos de los mismos datos (esto es para evitar una transacción no idempotente); seguir una mejor práctica bien definida es, bueno, la mejor práctica. El descanso no se define con una implementación específica en mente, lo que le permite usarlo como mejor le parezca. Sugeriría aprovechar todos los códigos de error de HTTP y los métodos de solicitud, pero se le permite hacerlo como lo desee
zzzzBov
44
Entonces, el problema con esta respuesta (es una respuesta decente, pero no completa) es que no aborda la pregunta principal que hizo: ¿Por qué usaría verbos HTTP y el URI en lugar de datos JSON personalizados (tal vez algún tipo de Sintaxis de invocación de API basada en JSON). Puede hacer su sintaxis JSON personalizada para que sea "inmediatamente ... aparente lo que está sucediendo con los datos". Lo que no puede hacer es utilizar fácilmente las funciones integradas y las capas de red sobre HTTP como puede hacerlo con una API que sigue todas las convenciones REST. No es que mi respuesta sea perfecta, por supuesto;)
Merlyn Morgan-Graham
44
@Andre: Los ejemplos que utiliza la entrada wiki son autenticación, almacenamiento en caché y negociación de tipo de contenido. Ahora que lo estoy pensando más, es posible que pueda utilizarlos con interfaces de estilo RPC, pero la tentación a menudo será implementar su propio sistema desde cero o codificar una integración en un sistema existente. Con REST puede usar la integración incorporada y administrarla en el servidor web. Esto significa un acoplamiento más flexible, lo que significa que tiene que implementar menos, y significa que su aplicación es mucho más flexible para cambiar las opciones en el futuro con menos código e impacto de prueba.
Merlyn Morgan-Graham
10
En lugar de BORRAR: / carros / más antiguo, ¿qué tal GET: / carros / más viejo seguido de DELETE? De esa manera, tienes dos comandos idempotentes por separado.
Neil
3
+1; Estoy de acuerdo en que esta es una buena respuesta (lo revisaré nuevamente por diversión y ganancias). POST: /cars/oldestser un reemplazo para un DELETE no tiene mucho sentido. Algo así como, POST: /cars/oldest/deletepodría, aunque creo que me gusta más la solución de Neil. La única ventaja que brinda una eliminación directa sobre su solución get-id-delete-id es la atomicidad. Quisiera una justificación comercial clara con un escenario no artificial antes de implementar tal cosa. No necesita admitir todos los verbos en todos los objetos / URL.
Merlyn Morgan-Graham
39

Esta es una pregunta de seguridad y mantenibilidad.

métodos seguros

Siempre que sea posible, debe utilizar métodos 'seguros' (unidireccionales) como GET y HEAD para limitar la vulnerabilidad potencial.

métodos idempotentes

Siempre que sea posible, debe utilizar métodos 'idempotentes' como GET, HEAD, PUT y DELETE, que no pueden tener efectos secundarios y, por lo tanto, son menos propensos a errores / más fáciles de controlar.

Fuente

Markus
fuente
1
Lo sentimos, pero ¿cómo son los métodos idempotentes PUT y DELETE? ¡Afectan el estado del servidor y sus datos!
Mahmoud Al-Qudsi
27
@Computer: Al hacer el mismo PUT o el mismo DELETE se obtiene el mismo estado final. Eso es lo que significa "idempotente".
Ignacio Vazquez-Abrams
44
Para más aclaraciones: una operación F es idempotente, si su aplicación única y sus varias aplicaciones consiguientes devuelven el mismo resultado. Más precisamente, F es idempotente si y solo si F (x) = F (F (x)). Por ejemplo, Eliminar es idempotente, porque cuando elimina un elemento una vez, o lo elimina varias veces, el resultado es el mismo: el elemento se elimina solo una vez con la primera aplicación de eliminación y no sucede nada en la segunda o tercera aplicación de eliminación.
qartal
1
Pero en términos de creación, cuando crea un nuevo registro con un comando de creación y vuelve a emitir el mismo comando, se crean (probablemente) dos registros (aunque ambos reflejan la misma información).
qartal
qartal: su definición funcional para idempotente debe ser 'F (X) = F (X) F (X)'. Buena forma de expresarlo sin embargo.
Gerard ONeill
26

En resumen, REST enfatiza los sustantivos sobre los verbos. A medida que su API se vuelve más compleja, agrega más cosas, en lugar de más comandos.

Neil
fuente
2
Tuve algunos problemas para entender esto. Este post ( lornajane.net/posts/2013/... ) que el verbo debe venir de la petición HTTP para que el URI debe entonces sólo contienen nombres sacó un poco para mí
icc97
9

Usted pregunto :

¿No sería más fácil aceptar el objeto JSON a través de $ _POST normal y luego responder también en JSON

De la Wikipedia en REST :

Las aplicaciones RESTful maximizan el uso de la interfaz bien definida y preexistente y otras capacidades integradas proporcionadas por el protocolo de red elegido, y minimizan la adición de nuevas características específicas de la aplicación.

Por lo que (poco) he visto, creo que esto generalmente se logra maximizando el uso de los verbos HTTP existentes y diseñando un esquema de URL para su servicio que sea lo más poderoso y evidente posible.

Los protocolos de datos personalizados (incluso si están construidos sobre los estándares, como SOAP o JSON) se desaconsejan y deben minimizarse para ajustarse mejor a la ideología REST.

SOAP RPC sobre HTTP, por otro lado, alienta a cada diseñador de aplicaciones a definir un vocabulario nuevo y arbitrario de sustantivos y verbos (por ejemplo, getUsers (), savePurchaseOrder (...)), generalmente superpuestos en el verbo HTTP 'POST'. Esto ignora muchas de las capacidades existentes de HTTP, como la autenticación, el almacenamiento en caché y la negociación del tipo de contenido, y puede hacer que el diseñador de la aplicación reinvente muchas de estas características dentro del nuevo vocabulario.

Los objetos reales con los que está trabajando pueden tener cualquier formato. La idea es reutilizar la mayor cantidad de HTTP posible para exponer las operaciones que el usuario desea realizar en esos recursos (consultas, gestión / mutación de estado, eliminación).

Usted pregunto :

¿Me estoy perdiendo de algo?

Hay mucho más que saber sobre REST y la sintaxis de URI / verbos HTTP. Por ejemplo, algunos de los verbos son idempotentes, otros no. No vi nada sobre esto en su pregunta, así que no me molesté en tratar de sumergirme en eso. Las otras respuestas y Wikipedia tienen mucha buena información.

Además, hay mucho que aprender sobre las diversas tecnologías de red creadas sobre HTTP que puede aprovechar si está utilizando una API verdaderamente relajante. Comenzaría con la autenticación.

Merlyn Morgan-Graham
fuente
8

Con respecto al uso de la extensión para definir el tipo de datos. Noté que la API de MailChimp lo está haciendo, pero no creo que sea una buena idea.

GET /zzz/cars.json/1

GET /zzz/cars.xml/1

Parece una buena idea, pero creo que el enfoque "más antiguo" es mejor: usar encabezados HTTP

GET /xxx/cars/1
Accept: application/json

También los encabezados HTTP son mucho mejores para la comunicación de tipo de datos cruzados (si alguna vez alguien lo necesitara)

POST /zzz/cars
Content-Type: application/xml     <--- indicates we sent XML to server
Accept: application/json          <--- indicates we want get data back in JSON format  
Pawel Cioch
fuente
4

¿Me estoy perdiendo de algo?

Si. ;-)

Este fenómeno existe debido a la restricción de interfaz uniforme . A REST le gusta usar estándares ya existentes en lugar de reinventar la rueda. El estándar HTTP ya ha demostrado ser altamente escalable (la web funciona por un tiempo). ¿Por qué debemos arreglar algo que no está roto?

nota: la restricción de interfaz uniforme es importante si desea desacoplar los clientes del servicio. Es similar a definir interfaces para clases para desacoplarlas unas de otras. De c. aquí la interfaz uniforme consiste en estándares como HTTP , tipos MIME , URI , RDF , vocabulario de datos vinculados , vocabulario de hidra , etc.

inf3rno
fuente
2

La buena semántica es importante en la programación.

La utilización de más métodos además de GET / POST será útil porque aumentará la legibilidad de su código y facilitará su mantenimiento.

¿Por qué?

Porque sabes que GET recuperará datos de tu API. Usted sabe que POST agregará nuevos datos a su sistema. Sabes que PUT hará actualizaciones. ELIMINAR eliminará filas, etc., etc.

Normalmente estructuro mis servicios web RESTFUL para que tenga una devolución de llamada de función llamada igual que el método.

Yo uso PHP, así que uso function_exists (creo que se llama). Si la función no existe, lanzo un 405 (MÉTODO NO PERMITIDO).

HumbleWebDev
fuente
2

Bill Venners: En la publicación de su blog titulada "Por qué falló REST", dijo que necesitamos los cuatro verbos HTTP: GET, POST, PUT y DELETE, y lamentó que los proveedores de navegadores solo GET y POST. "¿Por qué necesitamos los cuatro verbos? ¿Por qué no son suficientes GET y POST?

Elliotte Rusty Harold: Hay cuatro métodos básicos en HTTP: GET, POST, PUT y DELETE. GET se usa la mayor parte del tiempo. Se usa para cualquier cosa que sea segura, que no cause ningún efecto secundario. GET se puede marcar, guardar en caché, vincular y pasar a través de un servidor proxy. Es una operación muy poderosa, una operación muy útil.

POST por el contrario es quizás la operación más poderosa. Se puede hacer cualquier cosa. No hay límites en cuanto a lo que puede suceder, y como resultado, debes tener mucho cuidado con eso. No lo marque como favorito. No lo guardas en caché. No lo traes de antemano. No haces nada con una POST sin preguntarle al usuario. ¿Quieres hacer esto? Si el usuario presiona el botón, puede PUBLICAR algún contenido. Pero no va a mirar todos los botones de una página y comenzará a presionarlos al azar. Por el contrario, los navegadores pueden mirar todos los enlaces de la página y buscarlos previamente, o buscar los que creen que es más probable que sigan a continuación. Y, de hecho, algunos navegadores y extensiones de Firefox y otras herramientas han intentado hacerlo en un momento u otro.

PUT y DELETE están en el medio entre GET y POST. La diferencia entre PUT o DELETE y POST es que PUT y DELETE son * idempotentes, mientras que POST no lo es. PUT y DELETE se pueden repetir si es necesario. Digamos que está intentando cargar una nueva página en un sitio. Digamos que desea crear una nueva página en http://www.example.com/foo.html, así que escribe su contenido y lo PONE en esa URL. El servidor crea esa página en esa URL que usted proporciona. Ahora, supongamos por alguna razón que su conexión de red se cae. No está seguro, ¿se recibió o no la solicitud? Tal vez la red es lenta. Tal vez hubo un problema con el servidor proxy. Por lo tanto, está bien intentarlo de nuevo, o de nuevo, tantas veces como desee. Porque PONER el mismo documento en la misma URL diez veces no será diferente de ponerlo una vez. Lo mismo es cierto para DELETE. Puede BORRAR algo diez veces, y eso es lo mismo que borrarlo una vez.

Por el contrario, POST puede causar que ocurra algo diferente cada vez. Imagine que está saliendo de una tienda en línea presionando el botón comprar. Si vuelve a enviar esa solicitud POST, podría terminar comprando todo en su carrito por segunda vez. Si lo envía nuevamente, lo ha comprado por tercera vez. Es por eso que los navegadores deben tener mucho cuidado al repetir las operaciones POST sin el consentimiento explícito del usuario, porque POST puede causar que sucedan dos cosas si lo hace dos veces, tres cosas si lo hace tres veces. Con PUT y DELETE, hay una gran diferencia entre cero solicitudes y una, pero no hay diferencia entre una solicitud y diez.

Visite la url para más detalles. http://www.artima.com/lejava/articles/why_put_and_delete.html

Actualizar:

Métodos idempotentes Un método HTTP idempotente es un método HTTP que se puede llamar muchas veces sin resultados diferentes. No importaría si el método se llama solo una vez o diez veces. El resultado debería ser el mismo. Nuevamente, esto solo se aplica al resultado, no al recurso en sí. Esto todavía se puede manipular (como una marca de tiempo de actualización, siempre que esta información no se comparta en la representación de recursos (actual).

Considere los siguientes ejemplos:

a = 4;

a ++;

El primer ejemplo es idempotente: no importa cuántas veces ejecutemos esta declaración, a siempre será 4. El segundo ejemplo no es idempotente. Ejecutar esto 10 veces dará como resultado un resultado diferente que cuando se ejecuta 5 veces. Dado que ambos ejemplos están cambiando el valor de a, ambos son métodos no seguros.

Bimal Das
fuente
1
Sobre el ejemplo de una nueva página, ¿no se debe usar POST de esa manera, mientras se PONE una actualización? La creación de una nueva página es un proceso que produce un nuevo resultado cada vez, mientras que la misma edición se puede REPUTAR cualquier cantidad de veces, obteniendo el mismo resultado cada vez. Agradable fraseo y explicación, sin embargo.
Spyryto
0

Básicamente REST es ( wiki ):

  1. Arquitectura cliente-servidor
  2. Apatridia
  3. Caché
  4. Sistema de capas
  5. Código bajo demanda (opcional)
  6. Interfaz uniforme

REST no es protocolo, son principios. Diferentes uris y métodos, alguien llamado mejores prácticas.

MAX
fuente