Publi

Cómo crear una API RESTful en C++ y usarlo como queramos. [ Con código fuente y ejemplo disponible ]

photo-1441311956160-78a471e0638d

Las aplicaciones en Internet crecen a un ritmo frenético. Y para que éstas crezcan es necesario que unos sistemas se comuniquen con otros. Dentro de la misma aplicación, existe una comunicación con una base de datos, con un sistema de cachés con un servidor de correo, etc. Incluso dentro de una misma plataforma, existe una comunicación entre la capa de datos y la capa de interfaz de usuario (podemos verlo en aplicaciones móviles que acceden a datos alojados en un servidor web).

La forma de comunicarse (canal de comunicación, elementos a los que podemos acceder, quién puede acceder, etc), es a lo que llamamos API (o Application Programming Interface, Interfaz de Programación de Aplicaciones). Históricamente, estas APIs hacían referencia al diálogo entre programas o entre programa y biblioteca. Y lo encontramos continuamente a muchos niveles. Por ejemplo en los métodos que proporciona nuestro sistema operativo para leer y escribir un archivo: cuando programamos una aplicación normalmente no accedemos directamente a disco, hablamos con el sistema operativo para que nos simplifique la tarea; cuando accedemos a una webcam o scanner, tendremos un driver por encima que nos proporcionará un método sencillo para acceder a las imágenes; cuando se dibuja una ventana en nuestra pantalla, para que todas mantengan el mismo look&feel, accederemos a nuestro entorno de ventanas y le pediremos que dibuje una ventana; o cuando introducimos un evento de calendario desde nuestro teléfono móvil y éste aparezca al mismo tiempo en nuestra tablet y nuestro ordenador, las aplicaciones de cada dispositivo se comunicarán con un servidor para hacerlo.

Para comunicar nuestras aplicaciones por Internet hay muchas formas de hacerlo, es decir, las aplicaciones podrán utilizar muchos idiomas para dicha comunicación. La magia reside en que independientemente del lenguaje en el que estén programados, si se define bien una API para realizar esta comunicación, podremos realizar acciones en una aplicación web desde otra aplicación (escritorio, web, móvil, etc), o podemos, desde nuestra aplicación, acceder a Facebook, recursos de Google, Twitter o incluso bases de datos como la de Have I Been Pwned, bases de datos de geolocalización, letras de canciones, equivalencias, catálogos de tiendas online como Magento o Prestashop, etc.

Algo más sobre APIs Web

Para comunicarnos con otros servicios, podemos utilizar múltiples métodos, y uno de ellos es establecer una comunicación de red, y ya que para pedir algo por Internet, utilizamos TCP/IP. ¿ Por qué no usar lo mismo ? Bueno, y ya puestos, si para acceder a una web utilizamos el puerto 80, al que normalmente muchos firewalls están configurados para permitir y que, si nos pilla en alguna red extraña (facultades, redes de empresas, puntos de acceso públicos, etc) no vamos a tener problemas para conectar. Oye y, ¿ por qué no utilizamos el protocolo HTTP para comunicarnos con los servidores ? Haríamos peticiones como si fuera una página web, podríamos utilizar cookies de forma transparente, cabeceras, compresión y los navegadores lo entenderían, pues perfecto.
A partir de ahí encontramos muchas tecnologías que se han utilizado para establecer este tipo de comunicaciones. Por ejemplo SOAP (Simple Object Access Protocol), WSDL (Web Services Description Language) y últimamente REST (REpresentational state transfer) está muy de moda (aunque tiene ya su tiempo).

Una particularidad de las APIs REST, es que para varias operaciones se utiliza una URL de entrada similar, pero métodos HTTP diferentes según lo que queramos. En el ejemplo de más abajo, hemos descrito el recurso films que tendrá habilitados los métodos GET, POST, PUT, PATCH y DELETE, dependiendo de la operación que vayamos a realizar.

Bibliotecas utilizadas

Para el desarrollo de este proyecto he utilizado Glove, que es uno de mis proyectos, aunque podemos basarnos en él para utilizar otra biblioteca para interacción con la web. En la última actualización se pueden crear conexiones SSL de forma muy sencilla. Y JSON for Modern C++, aunque también lo podemos ver si clonamos el repositorio de Github de Glove.

El hecho de utilizar la biblioteca de json es para parsear las entradas de datos, las salidas son excesivamente simples y están puestas a mano, aunque bien podría haber utilizado esta gran biblioteca para procesarlas.

Nuestro ejemplo de API REST

El ejemplo que cuento a continuación (más abajo veremos el código fuente) trata de una base de datos de películas (En C++ lo haremos con un vector de un struct, pero lo podremos complicar aún más). Dicho vector lo hemos metido en una clase, y dicha clase (Cinema) tiene métodos para:

  • Listar los contenidos del vector (ver todas las películas)
  • Presentar datos de un elemento (ver una película)
  • Modificar todos los datos de un elemento (editar una película)
  • Parchear un elemento (editar un dato individual de una película)
  • Eliminar un elemento (eliminar una película)

Ya tenemos nuestro CRUD en C++. Al menos tenemos métodos sencillos para gestionar los elementos del vector, hacerlo directamente en las peticiones podrá ser más efectivo y más rápido, pero en este punto prefiero que el código se lea y se mantenga bien.

Luego se define un punto de entrada a nuestra api que será /films/ por lo que si hacemos:

  • GET /films/ : obtendremos un listado de películas de nuestra base de datos. En JSON.
  • GET /films/[id] : obtendremos los datos de la película con dicho ID. En JSON.
  • POST /films/ : se añade una película. Los datos entran en JSON y se devuelve un JSON con el estado y la URL donde podemos encontrar la película añadida
  • PUT /films/[id] : Modificamos la película con dicho ID cambiando todos sus elementos. Entrada en JSON y respuesta estado y URL como el método anterior.
  • PATCH /films/[id] : Editamos un elemento de la película con dicho ID. Entrada en JSON y respuesta similar al anterior.
  • DELETE /films/[id] : Eliminamos la película con el ID especificado. La respuesta será un status: ok en JSON.

Para hacer esto, crearemos una clase y un método para cada petición. En Glove, los métodos que atienden las peticiones llevarán dos argumentos, uno para la petición (request) y otro para la respuesta (response). Dicha clase utilizará por detrás un objeto de clase Cinema (la clase que hicimos antes).

La implementación

Aquí está el código fuente completo del ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include "glovehttpserver.hpp"
#include <iostream>
#include <chrono>
#include <thread>
#include <string>
#include <vector>
#include "json.hpp"

int atoi(std::string s)
{
    try
        {
            return std::stod(s);
        }
    catch (std::exception &e)
        {
            return 0;
        }
}

static std::string jsonkv(std::string k, std::string v)
{
    /* "k": "v" */
    return """+k+"": ""+v+""";
}

struct Film
{
    unsigned id;
    std::string title;
    std::string director;
    std::string stars;
    unsigned duration;
};

class Cinema
{
public:
    Cinema()
    {
    }
    ~Cinema()
    {
    }

    unsigned addFilm(std::string title, std::string director,
                                     std::string stars, unsigned duration)
    {
        unsigned id = currentId++;
        films.push_back({id, title, director, stars, duration});
        return id;
    }

    unsigned delFilm(unsigned id)
    {
        auto film = findFilm(id);
        if (film != films.end())
            return films.erase(film), id;
       
        return 0;
    }

    unsigned updateFilm(unsigned id, std::string title, std::string director,
                                            std::string stars, unsigned duration)
    {
        auto film = findFilm(id);
        if (film != films.end())
            {
                film->title = title;
                film->director = director;
                film->stars = stars;
                film->duration = duration;
                return id;
            }
        return 0;
    }

    unsigned patchFilm(unsigned id, std::string field, std::string value)
    {
        std::cout << "PATCHEO\n";
        auto film = findFilm(id);
        if (film != films.end())
            {
                std::cout << "TENGO\n";
                if (field == "title")
                    film->title = value;
                else if (field == "director")
                    film->director = value;
                else if (field == "stars")
                    film->stars = value;
                else if (field == "duration")
                    film->duration = atoi(value);
                std::cout << "PATCHEO\n";

                return id;
            }
        return 0;
    }

    std::string getFilm(unsigned id)
    {
        std::string out;
        auto film = findFilm(id);
        if (film != films.end())
            out=filmjson(*film);
        return out;
    }

    std::string getFilms()
    {
        if (films.size()==0)
            return "[\n]";
       
        std::string out;
        auto f = films.begin();
        out+=filmjson(*f);
        while (++f != films.end())
            out+=",\n"+filmjson(*f);
        return "[\n "+ out + "\n]";
    }
   
    size_t size()
    {
        return films.size();
    }
private:   
    static std::string filmjson(Film& f)
    {
        return "{ "
            + jsonkv("id", std::to_string(f.id))+",\n"
            + jsonkv("title", f.title)+",\n"
            + jsonkv("director", f.director)+",\n"
            + jsonkv("stars", f.stars)+",\n"
            + jsonkv("duration", std::to_string(f.duration))
            + " }";
    }
   
    std::vector<Film>::iterator findFilm(unsigned id)
    {
        for (auto f=films.begin(); f!=films.end(); ++f)
            {
                if (f->id==id)
                    return f;
            }
        return films.end();
    }
    static unsigned currentId;
    std::vector<Film> films;
};

unsigned Cinema::currentId=1;

class CinemaApi
{
public:
    CinemaApi()
    {
        /* Populate database */
        films.addFilm("Doctor Strange", "Scott Derrickson", "Rachel McAdams, Benedict Cumberbatch, Mads Mikkelsen", 115);
        films.addFilm("The Magnificent Seven", "Antoine Fuqua", "Denzel Washington, Chris Pratt, Ethan Hawke", 133);
        films.addFilm("Bridget Jones' Baby", "Sharon Maguire", " Renée Zellweger, Gemma Jones, Jim Broadbent", 123);
        films.addFilm("Snowden", "Oliver Stone", " Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo", 134);
        films.addFilm("Don't Breathe", "Fede Alvarez", "Stephen Lang, Jane Levy, Dylan Minnette", 88);
        films.addFilm("Suicide Squad", "David Ayer", "Will Smith, Jared Leto, Margot Robbie", 123);
    }  
   
    void get(GloveHttpRequest &request, GloveHttpResponse& response)
    {
        response.contentType("text/json");
        if (request.special["filmId"].empty())
            response << films.getFilms();
        else
            {
                auto res = films.getFilm(atoi(request.special["filmId"]));
                if (res.empty())
                    throw GloveApiException(3, "No films found");
                response << res;
            }
    }

    void post(GloveHttpRequest& request, GloveHttpResponse& response)
    {
        auto jsonInput = nlohmann::json::parse(request.getData());
        auto title = jsonInput["title"];
        auto director = jsonInput["director"];
        auto stars = jsonInput["stars"];
        auto duration = jsonInput["duration"];
        if (title.is_null())
            throw GloveApiException(1, "No title given");
        if (director.is_null())
            throw GloveApiException(1, "No director given");
        if (stars.is_null())
            throw GloveApiException(1, "No stars given");
        if (duration.is_null())
            throw GloveApiException(1, "No duration given");

        unsigned id = films.addFilm(title.get<std::string>(),
                                                            director.get<std::string>(),
                                                            stars.get<std::string>(),
                                                            duration.get<uint32_t>());
        if (!id)
            throw GloveApiException(1, "There was a problem adding film");
        auto targetUri = request.getUri().servicehost()+"/films/"+std::to_string(id);
        response << "{ "
                         << jsonkv("status", "ok") << ",\n"
                         << jsonkv("target", targetUri) << " }";
    }

    void put(GloveHttpRequest& request, GloveHttpResponse& response)
    {
        unsigned currentId = atoi(request.special["filmId"]);
        if (!currentId)
            throw GloveApiException(5, "Invalid id given");
        auto jsonInput = nlohmann::json::parse(request.getData());
        auto title = jsonInput["title"];
        auto director = jsonInput["director"];
        auto stars = jsonInput["stars"];
        auto duration = jsonInput["duration"];
        if (title.is_null())
            throw GloveApiException(1, "No title given");
        if (director.is_null())
            throw GloveApiException(1, "No director given");
        if (stars.is_null())
            throw GloveApiException(1, "No stars given");
        if (duration.is_null())
            throw GloveApiException(1, "No duration given");

        unsigned id = films.updateFilm(currentId,
                                                             title.get<std::string>(),
                                                             director.get<std::string>(),
                                                             stars.get<std::string>(),
                                                             duration.get<uint32_t>());
        if (!id)
            throw GloveApiException(1, "There was a problem updating the record");
       
        auto targetUri = request.getUri().servicehost()+"/films/"+std::to_string(id);
        response << "{ "
                         << jsonkv("status", "ok") << ",\n"
                         << jsonkv("target", targetUri) << " }";
    }

    void patch(GloveHttpRequest& request, GloveHttpResponse& response)
    {
        unsigned currentId = atoi(request.special["filmId"]);
        if (!currentId)
            throw GloveApiException(5, "Invalid id given");
        auto jsonInput = nlohmann::json::parse(request.getData());
        auto title = jsonInput["title"];
        auto director = jsonInput["director"];
        auto stars = jsonInput["stars"];
        auto duration = jsonInput["duration"];
        bool ok = true;
        if ( (ok) && (!title.is_null()) )
            ok = ok && films.patchFilm(currentId, "title", title.get<std::string>());
        if ( (ok) && (!director.is_null()) )
            ok = ok && films.patchFilm(currentId, "director", director.get<std::string>());
        if ( (ok) && (!stars.is_null()) )
            ok = ok && films.patchFilm(currentId, "stars", stars.get<std::string>());
        if ( (ok) && (!duration.is_null()) )
            ok = ok && films.patchFilm(currentId, "duration", std::to_string(duration.get<std::uint32_t>()));

        if (!ok)
            throw GloveApiException(1, "There was a problem updating the record");
       
        auto targetUri = request.getUri().servicehost()+"/films/"+std::to_string(currentId);
        response << "{ "
                         << jsonkv("status", "ok") << ",\n"
                         << jsonkv("target", targetUri) << " }";
    }

    void delet(GloveHttpRequest& request, GloveHttpResponse& response)
    {
        unsigned currentId = atoi(request.special["filmId"]);
        if (!currentId)
            throw GloveApiException(5, "Invalid id given");

        if (!films.delFilm(currentId))
            throw GloveApiException(8, "There was a problem deleting the record");

        response << "{ "+jsonkv("status", "ok")+" }";
    }

private:
    Cinema films;
};

int main(int argc, char *argv[])
{
    CinemaApi cine;

  GloveHttpServer serv(8080, "", 2048);
    namespace ph = std::placeholders;
  /* serv.addRoute("/films/$filmId", restFilm, 2, 1, { "GET", "POST", "PUT", "PATCH", "DELETE" }); */
    serv.addRest("/films/$filmId", 1,
                             GloveHttpServer::jsonApiErrorCall,
                             std::bind(&CinemaApi::get, &cine, ph::_1, ph::_2),
                             std::bind(&CinemaApi::post, &cine, ph::_1, ph::_2),
                             std::bind(&CinemaApi::put, &cine, ph::_1, ph::_2),
                             std::bind(&CinemaApi::patch, &cine, ph::_1, ph::_2),
                             std::bind(&CinemaApi::delet, &cine, ph::_1, ph::_2)
    );
  std::cout << "READY"<<std::endl;
  while(1)
    {
      std::this_thread::sleep_for(std::chrono::seconds(1));
    }

  std::cout << "TEST"<<std::endl;

}

Para compilar podemos hacer:

g++ -o apiexample2 apiexample.cc glovehttpserver.cpp glove.cpp glovewebsockets.cpp glovecoding.cpp glovehttpcommon.cpp -lpthread -DENABLE_OPENSSL=0 -DENABLE_COMPRESSION=0 -std=c++11

En la última versión de Glove, separé el código en varios archivos más, para tener rutinas de codificación y rutinas comunes de HTTP separadas y no hacer tan terribles las compilaciones y tan grandes los archivos fuente… y un archivo para soporte WebSocket… que ya comentaré un día de estos en otro post.

Por otro lado, en el ejemplo anterior estamos compilando sin soporte para SSL (-DENABLE_OPENSSL) ni compresión (-DENABLE_COMPRESSION). Aunque está bien activarlos para experimentar con estas capacidades también (activar SSL nos permite utilizar rutinas de criptografía nativas de OpenSSL que funcionan mejor que los reemplazos.
Para compilarlo con esos soportes debemos tener las bibliotecas libssl y zlib que seguramente encontréis en vuestra distribución (con cabeceras de desarrollo) y ejecutar:

g++ -o apiexample2 apiexample.cc glovehttpserver.cpp glove.cpp glovewebsockets.cpp glovecoding.cpp glovehttpcommon.cpp -lpthread -lssl -lcrypto -lz -std=c++11

Incluso podemos compilar con -O4 para optimizar el ejecutable resultante tanto en tamaño como en rendimiento.

Comprimiendo la salida

Para comprimir la salida generada, basta con especificar los métodos de compresión activados (por ahora sólo están implementados gzip y deflate) y pasarlos cuando estés creando el servidor:

1
2
 GloveHttpServer serv(8080, "", 2048);
 serv.compression("gzip, deflate");

Dedicaré otro post a la creación de servidores con OpenSSL activado desde aquí, aunque debemos tener mucho cuidado cuando en la salida figure parte de la entrada (no es el caso ahora mismo), y tengamos SSL y compresión activadas, ya que podríamos ser vulnerables antes BREACH.

Ejemplo de peticiones

Las peticiones que realicemos, las podremos hacer desde un script en PHP, desde Javascript, tanto en servidor como en cliente, de esta última manera será el usuario el que con su navegador haga las peticiones, desde Java, C, C++, o incluso desde Bash con cURL. Y, con este último voy a hacer la demostración. Para las pruebas, he iniciado el servidor en el puerto 8080 (aunque si queremos, podemos redirigir ciertas rutas de Apache a nuestro servidor y acceder desde el puerto 80, con lo que puede coexistir con lo que actualmente estemos sirviendo.
Sin más dilación pondré aquí algunas peticiones y su respuesta:

GET /films/

curl http://localhost:8080/films/
[
{ “id”: “1”,
“title”: “Doctor Strange”,
“director”: “Scott Derrickson”,
“stars”: “Rachel McAdams, Benedict Cumberbatch, Mads Mikkelsen”,
“duration”: “115” },
{ “id”: “2”,
“title”: “The Magnificent Seven”,
“director”: “Antoine Fuqua”,
“stars”: “Denzel Washington, Chris Pratt, Ethan Hawke”,
“duration”: “133” },
{ “id”: “3”,
“title”: “Bridget Jones’ Baby”,
“director”: “Sharon Maguire”,
“stars”: ” Renée Zellweger, Gemma Jones, Jim Broadbent”,
“duration”: “123” },
{ “id”: “4”,
“title”: “Snowden”,
“director”: “Oliver Stone”,
“stars”: ” Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo”,
“duration”: “134” },
{ “id”: “5”,
“title”: “Don’t Breathe”,
“director”: “Fede Alvarez”,
“stars”: “Stephen Lang, Jane Levy, Dylan Minnette”,
“duration”: “88” },
{ “id”: “6”,
“title”: “Suicide Squad”,
“director”: “David Ayer”,
“stars”: “Will Smith, Jared Leto, Margot Robbie”,
“duration”: “123” }
]

GET /films/4

curl http://localhost:8080/films/4
{ “id”: “4”,
“title”: “Snowden”,
“director”: “Oliver Stone”,
“stars”: ” Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo”,
“duration”: “134” }

POST /films/

curl -XPOST http://localhost:8080/films/ -d ‘{ “title” : “The Great Wall”, “director”: “Yimou Zhang”, “stars” : “Pedro Pascal, Matt Damon, Willem Dafoe”, “duration”: 120 }’
{ “status”: “ok”,
“target”: “http://localhost:8080/films/7” }

PUT /films/

curl -XPUT http://localhost:8080/films/5 -d ‘{ “title” : “Inferno”, “director”: “Ron Howard”, “stars” : “Tom Hanks, Felicity Jones, Ben Foster”, “duration”: 121 }’
{ “status”: “ok”,
“target”: “http://localhost:8080/films/5” }

PATCH /films/

curl -XPATCH http://localhost:8080/films/7 -d ‘{ “duration” : 124, “stars”: “Will Smith, Jared Leto, Margot Robbie, Viola Davis” }’
{ “status”: “ok”,
“target”: “http://localhost:8080/films/7” }

DELETE /films/

curl -XDELETE http://localhost:8080/films/2
{ “status”: “ok” }

GET /films/ – de nuevo

Y, para ver cómo ha quedado todo:

curl http://localhost:8080/films/
[
{ “id”: “1”,
“title”: “Doctor Strange”,
“director”: “Scott Derrickson”,
“stars”: “Rachel McAdams, Benedict Cumberbatch, Mads Mikkelsen”,
“duration”: “115” },
{ “id”: “3”,
“title”: “Bridget Jones’ Baby”,
“director”: “Sharon Maguire”,
“stars”: ” Renée Zellweger, Gemma Jones, Jim Broadbent”,
“duration”: “123” },
{ “id”: “4”,
“title”: “Snowden”,
“director”: “Oliver Stone”,
“stars”: ” Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo”,
“duration”: “134” },
{ “id”: “5”,
“title”: “Inferno”,
“director”: “Ron Howard”,
“stars”: “Tom Hanks, Felicity Jones, Ben Foster”,
“duration”: “121” },
{ “id”: “6”,
“title”: “Suicide Squad”,
“director”: “David Ayer”,
“stars”: “Will Smith, Jared Leto, Margot Robbie”,
“duration”: “123” },
{ “id”: “7”,
“title”: “The Great Wall”,
“director”: “Yimou Zhang”,
“stars”: “Will Smith, Jared Leto, Margot Robbie, Viola Davis”,
“duration”: “124” }
]

Lo que falta por implementar

Aunque con esta API podemos hacer muchas cosas, incluso algunas cosas de esta lista se pueden implementar sobre los callbacks de acciones, no estaría mal que, en el futuro fueran de forma nativa:

  • Utilizar códigos de salida adecuados, como “201 Created” en operaciones POST, o “204 No Content” en operaciones DELETE en lugar de devolver un JSON con status=ok.
  • Autentificación: lograr que el sistema sepa qué usuario está pidiendo qué cosa. Aquí tenemos varios métodos que podrán ser habilitados o deshabilitados. El primero es autentificación HTTP, la básica de toda la vida, luego se podrían crear métodos especiales de identificación de usuarios que se pueden hacer persistentes para todas las peticiones de un mismo usuario mediante cookies o cabeceras especiales. Como último paso se podría implementar OAuth2, y darse un permiso temporal a una aplicación/usuario.
  • Autorización de peticiones. Ahora mismo todo está permitido, por todo el mundo, aunque en el futuro debemos decidir qué usuarios tendrán capacidad para qué cosas. Esto puede ser peligroso ya que el momento en el que sabemos si algo se puede hacer o no puede variar. Es decir, a veces podremos determinar que un usuario no puede hacer una petición DELETE y por tanto podremos denegar el acceso antes de llegar al método de acción. Otras veces, tal vez un usuario puede hacer DELETE pero sólo sobre determinados objetos…
  • Invalidación de métodos (method override). En determinados sistemas, sólo GET y POST están permitidos (sobre todo si pasamos a través de proxys), por lo que se implementa la cabecera especial X-HTTP-Method-Override y sólo en peticiones POST, esto actúa como una petición nativa del tipo especificado:

    POST /films/54
    X-HTTP-Method-Override: DELETE

  • Limitación de tráfico. Devolver un 429 Too Many Requests cuando un usuario esté pidiendo demasiado
  • Cacheo. El resultado de peticiones GET podrá ser almacenado temporalmente en memoria o en algún motor de caché en lugar de calcularse. Esto será útil cuando las peticiones sean más complejas y la cantidad de datos mucho más grande. En este ejemplo no tiene mucho sentido.

Para leer más

Si te has quedado con ganas de más, te dejo algunas cosas:

Opiniones, colaboración

Si te animas, puedes dejar un comentario con tus ideas, sugerencias, comentarios, errores de compilación, etc. Y si te animas más y creas algún ejemplo más, ponte en contacto conmigo, y pongo tus ejemplos en la web. Y si te animas aún más y quieres colaborar con Glove, hazle un fork en GitHub y ¡¡empieza a picar!!

Foto principal: Berry Van Der Velden

También podría interesarte...

Leave a Reply