La API RESTful consta de dos conceptos principales: Recurso, y Representación. El recurso puede ser cualquier objeto asociado con datos, o identificado con un URI (más de un URI puede referirse al mismo recurso), y puede operarse utilizando métodos HTTP. La representación es la forma en que se muestra el recurso. En este tutorial cubriremos información teórica sobre el diseño de la API RESTful e implementaremos una API de aplicación de blogging de ejemplo utilizando NodeJS.
Elegir los recursos correctos para una API RESTful es una sección importante del diseño. En primer lugar, debe analizar el dominio de su negocio y luego decidir cuántos y qué tipo de recursos se utilizarán que sean relevantes para su necesidad comercial. Si está diseñando una API de blogs, probablemente utilizará Artículo, Usuario, y Comentario. Esos son los nombres de recursos, y los datos asociados con eso son los recursos en sí mismos:
"title": "Cómo diseñar la API RESTful", "contenido": "El diseño de la API RESTful es un caso muy importante en el mundo del desarrollo de software", "autor": "huseyinbabal", "etiquetas": ["tecnología" , "nodejs", "node-restify"] "category": "NodeJS"
Puede continuar con una operación de recursos una vez que haya decidido los recursos necesarios. La operación aquí se refiere a los métodos HTTP. Por ejemplo, para crear un artículo, puede realizar la siguiente solicitud:
POST / articles HTTP / 1.1 Host: localhost: 3000 Content-Type: application / json "title": "Diseño de API RESTful con Restify", "slug": "restful-api-design-with-restify", "content" : "Pellentesque morbi tristique senectus et netus et malesuada fames ac turpis egestas.", "Autor": "huseyinbabal"
De la misma manera, puede ver un artículo existente emitiendo la siguiente solicitud:
GET / articles / 123456789012 HTTP / 1.1 Host: localhost: 3000 Tipo de contenido: application / json
¿Qué hay de actualizar un artículo existente? Puedo escuchar que estás diciendo:
Puedo hacer otra solicitud POST a / articles / update / 123456789012 con la carga útil.
Tal vez sea preferible, pero la URI es cada vez más compleja. Como dijimos anteriormente, las operaciones pueden referirse a métodos HTTP. Esto significa, indicar el actualizar Operación en el método HTTP en lugar de poner eso en el URI. Por ejemplo:
PUT / articles / 123456789012 HTTP / 1.1 Host: localhost: 3000 Content-Type: application / json "title": "Actualizado cómo diseñar la API RESTful", "content": "El diseño actualizado de la API RESTful es un caso muy importante en el mundo del desarrollo de software. "," author ":" huseyinbabal "," tags ": [" technology "," nodejs "," restify "," one tag más "]" category ":" NodeJS "
Por cierto, en este ejemplo ves etiquetas y campos de categoría. Esos no necesitan ser campos obligatorios. Puedes dejarlos en blanco y configurarlos en el futuro..
A veces, es necesario eliminar un artículo cuando está desactualizado. En ese caso puedes usar un BORRAR Solicitud HTTP a / artículos / 123456789012.
Los métodos HTTP son conceptos estándar. Si los usa como una operación, tendrá URI simples, y este tipo de API simple lo ayudará a obtener consumidores felices.
¿Qué pasa si quieres insertar un comentario a un artículo? Puede seleccionar el artículo y agregar un nuevo comentario al artículo seleccionado. Al utilizar esta declaración, puede utilizar la siguiente solicitud:
POST / articles / 123456789012 / comments HTTP / 1.1 Host: localhost: 3000 Content-Type: application / json "text": "Wow! Este es un buen tutorial", "author": "john doe"
La forma de recurso anterior se llama como sub-recurso. Comentario es un sub-recurso de Artículo. los Comentario la carga útil anterior se insertará en la base de datos como un hijo de Artículo. A veces, un URI diferente se refiere al mismo recurso. Por ejemplo, para ver un comentario específico, puede utilizar:
GET / articles / 123456789012 / comments / 123 HTTP / 1.1 Host: localhost: 3000 Tipo de contenido: application / json
o:
GET / comments / 123456789012 HTTP / 1.1 Host: localhost: 3000 Tipo de contenido: application / json
En general, las características de la API cambian con frecuencia para proporcionar nuevas características a los consumidores. En ese caso, pueden existir dos versiones de la misma API al mismo tiempo. Para separar esas dos características, puede usar el control de versiones. Hay dos formas de versionar
/v1.1/articles/123456789012
. GET / articles / 123456789012 HTTP / 1.1 Host: localhost: 3000 Versión de aceptación: 1.0
En realidad, la versión cambia solo la representación del recurso, no el concepto del recurso. Por lo tanto, no es necesario cambiar la estructura de URI. En v1.1, tal vez se haya agregado un nuevo campo al artículo. Sin embargo, todavía devuelve un artículo. En la segunda opción, el URI es aún simple y los consumidores no necesitan cambiar su URI en las implementaciones del lado del cliente.
Es importante diseñar una estrategia para situaciones en las que el consumidor no proporciona un número de versión. Puede generar un error cuando no se proporciona la versión, o puede devolver una respuesta utilizando la primera versión. Si usa la última versión estable como predeterminada, los consumidores pueden obtener muchos errores para sus implementaciones del lado del cliente.
La representación es la forma en que una API muestra el recurso. Cuando llama a un punto final de la API, se le devolverá un recurso. Este recurso puede estar en cualquier formato como XML, JSON, etc. JSON es preferible si está diseñando una nueva API. Sin embargo, si está actualizando una API existente que solía devolver una respuesta XML, puede proporcionar otra versión para una respuesta JSON.
Es suficiente información teórica sobre el diseño de RESTful API. Veamos el uso de la vida real diseñando e implementando una API de blogs con Restify..
Para diseñar una API RESTful, necesitamos analizar el dominio de negocios. Entonces podemos definir nuestros recursos. En una API de blogs, necesitamos:
En esta API, no cubriré cómo autenticar a un usuario para crear un artículo o comentario. Para la parte de autenticación, puede consultar el tutorial de autenticación basada en token con AngularJS y NodeJS.
Nuestros nombres de recursos están listos. Las operaciones de recursos son simplemente CRUD. Puede consultar la siguiente tabla para ver un escaparate general de API.
Nombre del recurso | Verbos HTTP | Métodos HTTP |
---|---|---|
Artículo | crear artículo actualizar artículo borrar artículo ver artículo | POST / artículos con carga útil PUT / articles / 123 con Payload ELIMINAR / artículos / 123 GET / artículo / 123 |
Comentario | crear comentario actualizar Coment Eliminar comentario ver comentario | POST / artículos / 123 / comentarios con carga útil PUT / comments / 123 con Payload ELIMINAR / COMENTARIOS / 123 GET / comentarios / 123 |
Usuario | crear usuario actualizar usuario borrar usuario ver Usuario | POST / usuarios con carga útil PUT / usuarios / 123 con carga útil ELIMINAR / usuarios / 123 GET / usuarios / 123 |
En este proyecto utilizaremos NodeJS con Restificar. Los recursos se guardarán en el MongoDB base de datos. En primer lugar, podemos definir los recursos como modelos en Restify..
var mongoose = require ("mongoose"); Esquema var = mangosta.Schema; var ArticleSchema = new Schema (title: String, slug: String, content: String, author: type: String, ref: "User"); mongoose.model ('Artículo', ArticleSchema);
var mongoose = require ("mongoose"); Esquema var = mangosta.Schema; var CommentSchema = new Schema (text: String, article: type: String, ref: "Article", autor: type: String, ref: "User"); mongoose.model ('Comment', CommentSchema);
No habrá ninguna operación para el recurso Usuario. Asumiremos que ya conocemos al usuario actual que podrá operar en artículos o comentarios.
Puedes preguntar de dónde viene este módulo de mangosta. Es el marco ORM más popular para MongoDB escrito como un módulo NodeJS. Este módulo está incluido en el proyecto dentro de otro archivo de configuración..
Ahora podemos definir nuestros verbos HTTP para los recursos anteriores. Puedes ver lo siguiente:
var restify = require ('restify'), fs = require ('fs') var controllers = , controllers_path = process.cwd () + '/ app / controllers' fs.readdirSync (controllers_path) .forEach (function (archivo ) if (file.indexOf ('. js')! = -1) controllers [file.split ('.') [0]] = require (controllers_path + '/' + file)) var server = restify.createServer (); server .use (restify.fullResponse ()) .use (restify.bodyParser ()) // Article Start server.post ("/ articles", controllers.article.createArticle) server.put ("/ articles /: id", controllers.article.updateArticle) server.del ("/ articles /: id", controllers.article.deleteArticle) server.get (ruta: "/ articles /: id", versión: "1.0.0", controladores. article.viewArticle) server.get (path: "/ articles /: id", versión: "2.0.0", controllers.article.viewArticle_v2) // Artículo Fin // Comentario Iniciar server.post ("/ comments" , controllers.comment.createComment) server.put ("/ comments /: id", controllers.comment.viewComment) server.del ("/ comments /: id", controllers.comment.deleteComment) server.get ("/ comments /: id ", controllers.comment.viewComment) // Comment End var port = process.env.PORT || 3000; server.listen (puerto, función (err) si (err) console.error (err) else console.log ('La aplicación está lista en:' + puerto)) if (process.env.environment == 'production' ) process.on ('uncaughtException', function (err) console.error (JSON.parse (JSON.stringify (err, ['stack', 'message', 'inner'], 2)))))
En este fragmento de código, primero de todos los archivos de controlador que contienen métodos de controlador se iteran y todos los controladores se inicializan para ejecutar una solicitud específica al URI. Después de eso, se definen los URI para operaciones específicas para las operaciones básicas de CRUD. También hay versiones de una de las operaciones en el artículo.
Por ejemplo, si declara la versión como 2
en el encabezado de aceptar versión, verArtículo_v2
será ejecutado. verArtículo
y verArtículo_v2
ambos hacen el mismo trabajo, mostrando el recurso, pero muestran el recurso del Artículo en un formato diferente, como puede ver en el título
campo abajo Finalmente, el servidor se inicia en un puerto específico y se aplican algunas comprobaciones de informe de errores. Podemos proceder con métodos de controlador para operaciones HTTP en recursos..
var mongoose = require ('mongoose'), Article = mongoose.model ("Article"), ObjectId = mongoose.Types.ObjectId exports.createArticle = function (req, res, next) var ArticleModel = new Article (req.body ); articleModel.save (function (err, article) if (err) res.status (500); res.json (type: false, data: "Ocurrió un error:" + err) else res.json ( type: true, data: article)) exports.viewArticle = function (req, res, next) Article.findById (new ObjectId (req.params.id), function (err, article) if ( err) res.status (500); res.json (type: false, data: "Ocurrió un error:" + err) else if (article) res.json (type: true, data: article ) else res.json (tipo: falso, datos: "Artículo:" + req.params.id + "no encontrado")) exports.viewArticle_v2 = function (req, res, next) Article.findById (nuevo ObjectId (req.params.id), función (err, article) if (err) res.status (500); res.json (type: false, data: "Ocurrió un error:" + err) else if (article) article.title = article.title + "v2" res.json (type: true, data: article) else res.json (type: false, data : "Artículo:" + req.params.id + "no encontrado")) exports.updateArticle = function (req, res, next) var updatedArticleModel = nuevo artículo (req.body); Article.findByIdAndUpdate (nuevo ObjectId (req.params.id), updatedArticleModel, function (err, article) if (err) res.status (500); res.json (type: false, data: "Ocurrió un error: "+ err) else if (article) res.json (type: true, data: article) else res.json (type: false, data:" Article: "+ req.params. id + "no encontrado")) exports.deleteArticle = function (req, res, next) Article.findByIdAndRemove (new Object (req.params.id), function (err, article) if (err ) res.status (500); res.json (type: false, data: "Ocurrió un error:" + err) else res.json (type: true, data: "Article:" + req. params.id + "eliminado con éxito"))
Puede encontrar una explicación de las operaciones básicas de CRUD en el lado de Mangosta a continuación:
articuloModelo
enviado desde el cuerpo de la solicitud. Se puede crear un nuevo modelo pasando el cuerpo de la solicitud como un constructor a un modelo como var articleModel = nuevo artículo (req.body)
. Encuentra uno
con un parámetro de ID es suficiente para devolver el detalle del artículo.salvar
mando.findByIdAndRemove
es la mejor manera de eliminar un artículo proporcionando la ID del artículo.Los comandos de Mongoose mencionados anteriormente son simplemente un método de tipo estático a través del objeto Article que también es una referencia del esquema de Mongoose.
var mongoose = require ('mongoose'), Comment = mongoose.model ("Comment"), Article = mongoose.model ("Article"), ObjectId = mongoose.Types.ObjectId exports.viewComment = function (req, res) Article.findOne ("comments._id": nuevo ObjectId (req.params.id), "comments. $": 1, function (err, comment) if (err) res.status (500) ; res.json (type: false, data: "Ocurrió un error:" + err) else if (comment) res.json (type: true, data: new Comment (comment.comments [0]) ) else else res.json (type: false, data: "Comment:" + req.params.id + "no encontrado")) exports.updateComment = function (req, res, next) var updatedCommentModel = new Comment (req.body); console.log (updatedCommentModel) Article.update ("comments._id": new ObjectId (req.params.id), "$ set": "comments. $. text": updatedCommentModel.text, "comments. $ .author ": updatedCommentModel.author, function (err) if (err) res.status (500); res.json (type: false, data:" Ocurrió un error: "+ err) else res.json (type: true, data: "Comment:" + req.params.id + "updated")) exports.deleteComment = function (req, res, next) Article.findOneAndUpdate ( "comments._id": nuevo ObjectId (req.params.id), "$ pull": "comments": "_id": nuevo ObjectId (req.params.id), function (err, article) if (err) res.status (500); res.json (type: false, data: "Ocurrió un error:" + err) else if (article) res.json (type: true, data: article) else res.json (type: false, data: "Comment:" + req.params.id + "no encontrado"))
Cuando realice una solicitud a uno de los URI de recursos, se ejecutará la función relacionada establecida en el controlador. Cada función dentro de los archivos del controlador puede usar el req y res objetos. los comentario recurso aquí es un sub-recurso de Artículo. Todas las operaciones de consulta se realizan a través del modelo del artículo para encontrar un documento secundario y realizar la actualización necesaria. Sin embargo, siempre que intente ver un recurso de Comentarios, verá uno incluso si no hay una colección en MongoDB.
/ artículos / 123
(Bueno), / artículos? id = 123
(Malo).Finalmente, si diseña una API RESTful siguiendo estas reglas fundamentales, siempre tendrá un sistema flexible, fácil de mantener y fácilmente comprensible..