L'API RESTful comprend deux concepts principaux: Ressource, et Représentation. La ressource peut être n'importe quel objet associé à des données ou identifié avec un URI (plusieurs URI peuvent faire référence à la même ressource) et peut être exploitée à l'aide de méthodes HTTP. La représentation est la façon dont vous affichez la ressource. Dans ce didacticiel, nous aborderons certaines informations théoriques sur la conception d'API RESTful et implémenterons un exemple d'API d'application de blogging à l'aide de NodeJS.
Le choix des ressources appropriées pour une API RESTful est une partie importante de la conception. Tout d'abord, vous devez analyser votre domaine d'activité, puis décider du nombre et du type de ressources qui seront utilisées et qui répondent à vos besoins. Si vous concevez une API de blogging, vous utiliserez probablement Article, Utilisateur, et Commentaire. Ce sont les noms de ressources et les données associées sont la ressource elle-même:
"title": "Comment concevoir une API RESTful", "content": "La conception d'une API RESTful est un cas très important dans le monde du développement logiciel.", "author": "huseyinbabal", "tags": ["technology" , "nodejs", "node-restify"] "category": "NodeJS"
Vous pouvez procéder à une opération de ressource après avoir déterminé les ressources requises. L'opération ici fait référence aux méthodes HTTP. Par exemple, pour créer un article, vous pouvez faire la demande suivante:
POST / articles HTTP / 1.1 Hôte: localhost: 3000 Content-Type: application / json "title": "Conception d'API RESTful avec Restify", "slug": "restful-api-design-with-restify", "contenu" : "Pellentesque habitant morbi tristique senectus et netus et malesuada célèbres ac turpis egestas.", "Auteur": "huseyinbabal"
De la même manière, vous pouvez visualiser un article existant en émettant la requête suivante:
GET / articles / 123456789012 HTTP / 1.1 Hôte: localhost: 3000 Type de contenu: application / json
Qu'en est-il de la mise à jour d'un article existant? Je peux entendre que vous dites:
Je peux faire une autre demande POST à / articles / update / 123456789012 avec la charge utile.
Peut-être préférable, mais l'URI devient de plus en plus complexe. Comme nous l'avons dit précédemment, les opérations peuvent faire référence à des méthodes HTTP. Cela signifie, indiquez le mettre à jour opération dans la méthode HTTP au lieu de mettre cela dans l'URI. Par exemple:
PUT / articles / 123456789012 HTTP / 1.1 Hôte: localhost: 3000 Type de contenu: application / json "title": "Mise à jour Comment concevoir une API RESTful", "content": "La mise à jour de la conception d'une API RESTful est un cas très important monde du développement logiciel. "," author ":" huseyinbabal "," tags ": [" technologie "," nodejs "," restify "," un tag supplémentaire "]" category ":" NodeJS "
À propos, dans cet exemple, vous voyez des balises et des champs de catégorie. Ceux-ci n'ont pas besoin d'être des champs obligatoires. Vous pouvez les laisser vides et les définir à l'avenir.
Parfois, vous devez supprimer un article lorsqu'il est obsolète. Dans ce cas, vous pouvez utiliser un EFFACER Demande HTTP à / articles / 123456789012.
Les méthodes HTTP sont des concepts standard. Si vous les utilisez en tant qu'opération, vous aurez des URI simples, et ce type d'API simple vous aidera à gagner des consommateurs satisfaits..
Et si vous voulez insérer un commentaire dans un article? Vous pouvez sélectionner l'article et ajouter un nouveau commentaire à l'article sélectionné. En utilisant cette instruction, vous pouvez utiliser la requête suivante:
POST / articles / 123456789012 / comments HTTP / 1.1 Hôte: localhost: 3000 Type de contenu: application / json "text": "Wow! C'est un bon tutoriel", "author": "john doe"
La forme de ressource ci-dessus est appelée en tant que sous-ressource. Commentaire est une sous-ressource de Article. le Commentaire la charge ci-dessus sera insérée dans la base de données en tant qu’enfant de Article. Parfois, un URI différent fait référence à la même ressource. Par exemple, pour afficher un commentaire spécifique, vous pouvez utiliser:
GET / articles / 123456789012 / comments / 123 HTTP / 1.1 Hôte: localhost: 3000 Type de contenu: application / json
ou:
GET / comments / 123456789012 HTTP / 1.1 Hôte: localhost: 3000 Type de contenu: application / json
En général, les fonctionnalités de l'API changent fréquemment afin de fournir de nouvelles fonctionnalités aux consommateurs. Dans ce cas, deux versions de la même API peuvent exister simultanément. Pour séparer ces deux fonctionnalités, vous pouvez utiliser le contrôle de version. Il existe deux formes de versioning
/v1.1/articles/123456789012
. GET / articles / 123456789012 HTTP / 1.1 Hôte: localhost: 3000 Accepter-Version: 1.0
En réalité, la version ne change que la représentation de la ressource, pas le concept de la ressource. Donc, vous n'avez pas besoin de changer la structure d'URI. Dans la v1.1, un nouveau champ a peut-être été ajouté à Article. Cependant, il retourne toujours un article. Dans la deuxième option, l'URI reste simple et les consommateurs n'ont pas besoin de changer d'URI dans les implémentations côté client..
Il est important de concevoir une stratégie pour les situations où le consommateur ne fournit pas de numéro de version. Vous pouvez générer une erreur lorsque la version n'est pas fournie ou vous pouvez renvoyer une réponse à l'aide de la première version. Si vous utilisez la dernière version stable par défaut, les utilisateurs peuvent obtenir de nombreuses erreurs pour leurs implémentations côté client..
La représentation est la façon dont une API affiche la ressource. Lorsque vous appelez un point de terminaison d'API, une ressource vous sera renvoyée. Cette ressource peut être dans n'importe quel format tel que XML, JSON, etc. JSON est préférable si vous concevez une nouvelle API. Toutefois, si vous mettez à jour une API existante qui renvoyait une réponse XML, vous pouvez fournir une autre version pour une réponse JSON..
C'est assez d'informations théoriques sur la conception d'API RESTful. Jetons un coup d'œil à l'utilisation en situation réelle en concevant et en mettant en œuvre une API de blogging à l'aide de Restify.
Pour concevoir une API RESTful, nous devons analyser le domaine métier. Ensuite, nous pouvons définir nos ressources. Dans une API de blogging, nous avons besoin de:
Dans cette API, je ne décrirai pas comment authentifier un utilisateur pour créer un article ou un commentaire. Pour la partie authentification, vous pouvez vous référer au tutoriel sur l’authentification basée sur les jetons avec AngularJS & NodeJS..
Nos noms de ressources sont prêts. Les opérations sur les ressources sont simplement CRUD. Vous pouvez vous reporter au tableau suivant pour une présentation générale de l'API..
Nom de la ressource | Verbes HTTP | Méthodes HTTP |
---|---|---|
Article | créer un article mettre à jour l'article supprimer l'article voir l'article | POST / articles avec charge utile PUT / articles / 123 avec charge utile SUPPRIMER / articles / 123 GET / article / 123 |
Commentaire | créer un commentaire mettre à jour le commentaire supprimer le commentaire voir le commentaire | POST / articles / 123 / comments with Payload PUT / comments / 123 avec charge utile SUPPRIMER / commentaires / 123 GET / comments / 123 |
Utilisateur | Créer un utilisateur mettre à jour l'utilisateur Supprimer l'utilisateur voir l'utilisateur | POST / utilisateurs avec charge utile PUT / utilisateurs / 123 avec charge utile SUPPRIMER / utilisateurs / 123 GET / utilisateurs / 123 |
Dans ce projet, nous utiliserons NodeJS avec Restifier. Les ressources seront sauvegardées dans le MongoDB base de données. Tout d’abord, nous pouvons définir des ressources en tant que modèles dans Restify..
var mangouste = require ("mangouste"); var Schema = mongoose.Schema; var ArticleSchema = new Schema (titre: String, slug: String, contenu: String, auteur: type: String, ref: "Utilisateur"); mongoose.model ('Article', ArticleSchema);
var mangouste = require ("mangouste"); var Schema = mongoose.Schema; var CommentSchema = new Schema (text: String, article: type: String, ref: "Article", auteur: type: String, ref: "User"); mongoose.model ('Comment', CommentSchema);
Il n'y aura aucune opération pour la ressource User. Nous supposerons que nous connaissons déjà l'utilisateur actuel qui sera capable d'opérer des articles ou des commentaires.
Vous pouvez demander d'où vient ce module mangouste. C'est le framework ORM le plus populaire pour MongoDB, écrit en tant que module NodeJS. Ce module est inclus dans le projet dans un autre fichier de configuration..
Nous pouvons maintenant définir nos verbes HTTP pour les ressources ci-dessus. Vous pouvez voir ce qui suit:
var restify = require ('restify'), fs = require ('fs') var controllers = , controllers_path = process.cwd () + '/ app / controllers' fs.readdirSync (controllers_path) .forEach (fonction (fichier ) if (file.indexOf ('. js')! = -1) controllers [file.split ('.') [0]] = require (chemin_contrôleur + '/' + fichier)) var serveur = restify.createServer (); server .use (restify.fullResponse ()) .use (restify.bodyParser ()) // Article Démarrer server.post ("/ articles", controllers.article.createArticle) server.put ("/ articles /: id", controllers.article.updateArticle) server.del ("/ articles /: id", controllers.article.deleteArticle) server.get (chemin: "/ articles /: id", version: "1.0.0", contrôleurs. article.viewArticle) server.get (chemin: "/ articles /: id", version: "2.0.0", controllers.article.viewArticle_v2) // Article End // Commentaire Démarrer 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) // Commentaire End var port = process.env.PORT || 3000; server.listen (port, fonction (err) if (err) console.error (err) sinon console.log ('L'application est prête à:' + port)) if (process.env.environment == 'production' ) process.on ("uncaughtException", fonction (err) console.error (JSON.parse (JSON.stringify (err, ["pile", "message", "intérieur"], 2))))
Dans cet extrait de code, les fichiers de contrôleur contenant des méthodes de contrôleur sont tout d'abord itérés et tous les contrôleurs sont initialisés afin d'exécuter une requête spécifique à l'URI. Ensuite, les URI pour des opérations spécifiques sont définis pour les opérations CRUD de base. Il existe également une gestion des versions pour l’une des opérations sur Article..
Par exemple, si vous déclarez version comme 2
dans l'en-tête Accept-Version, voirArticle_v2
sera exécuté. voirArticle
et voirArticle_v2
les deux font le même travail, montrant la ressource, mais ils affichent la ressource Article dans un format différent, comme vous pouvez le voir dans le Titre
Champ ci-dessous. Enfin, le serveur est démarré sur un port spécifique et certaines vérifications de rapport d'erreur sont appliquées. Nous pouvons poursuivre avec les méthodes du contrôleur pour les opérations HTTP sur les ressources.
var mongoose = require ('mongoose'), Article = mongoose.model ("Article"), ObjectId = mongoose.Types.ObjectId exports.createArticle = fonction (req, res, suivant) var articleModel = nouvel article (req.body ) articleModel.save (function (err, article) if (err) res.status (500); res.json (type: false, data: "Une erreur s'est produite:" + err) else res.json ( type: true, données: article) exports.viewArticle = fonction (req, res, suivant) Article.findById (nouvel ObjectId (req.params.id), fonction (err, article) if ( err) res.status (500); res.json (type: false, data: "Une erreur s'est produite:" + err) else if (article) res.json (type: true, data: article ) else res.json (type: false, données: "Article:" + req.params.id + "non trouvé")) exports.viewArticle_v2 = fonction (req, res, next) Article.findById (nouvel ObjectId (req.params.id), fonction (err, article) if (err) res.status (500); res.json (type: false, données: "une erreur s'est produite:" + err) else if (article) article.title = article.title + "v2" res.json (type: true, données: article) else res.json (type: false, données : "Article:" + req.params.id + "non trouvé")) exports.updateArticle = fonction (req, res, next) var updatedArticleModel = new Article (req.body); Article.findByIdAndUpdate (nouvel ObjectId (req.params.id), updatedArticleModel, function (err, article) if (err) res.status (500); res.json (type: false, données: "Une erreur s'est produite: "+ err) else if (article) res.json (type: true, données: article) else res.json (type: false, données:" Article: "+ req.params. id + "non trouvé")) exports.deleteArticle = fonction (req, res, suivant) Article.findByIdAndRemove (nouvel objet (req.params.id), fonction (err, article) if (err ) res.status (500); res.json (type: false, données: "Une erreur s'est produite:" + err) else res.json (type: true, données: "Article:" + req. params.id + "supprimé avec succès"))
Vous pouvez trouver une explication des opérations de base du CRUD du côté Mongoose ci-dessous:
articleModèle
envoyé à partir du corps de la demande. Un nouveau modèle peut être créé en transmettant le corps de la demande en tant que constructeur à un modèle tel que var articleModel = nouvel article (req.body)
. findOne
avec un paramètre ID suffit pour retourner le détail de l'article.enregistrer
commander.findByIdAndRemove
est le meilleur moyen de supprimer un article en fournissant l'identifiant de l'article.Les commandes Mongoose mentionnées ci-dessus sont simplement statiques, comme méthode à travers un objet Article qui est également une référence du schéma 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": nouvel ObjectId (req.params.id), "comments. $": 1, fonction (err, comment) if (err) res.status (500) ; res.json (type: false, data: "Une erreur s'est produite:" + err) else if (comment) res.json (type: true, data: new Comment (comment.comments [0]) ) else res.json (type: false, données: "Commentaire:" + req.params.id + "non trouvé")) exports.updateComment = fonction (req, res, next) var updatedCommentModel = new Comment (req.body); console.log (updatedCommentModel) Article.update ("comments._id": nouvel ObjectId (req.params.id), "$ set": "comments. $. text": updatedCommentModel.text, "commentaires. $ .author ": updatedCommentModel.author, function (err) if (err) res.status (500); res.json (type: false, data:" Une erreur s'est produite: "+ err) else res.json (type: true, données: "Commentaire:" + req.params.id + "mis à jour")) exports.deleteComment = fonction (req, res, suivant) Article.findOneAndUpdate ( "comments._id": new ObjectId (req.params.id), "$ pull": "comments": "_id": new ObjectId (req.params.id), function (err, article) if (err) res.status (500); res.json (type: false, data: "Une erreur s'est produite:" + err) else if (article) res.json (type: true, data: article) else res.json (type: false, data: "Comment:" + req.params.id + "not found"))
Lorsque vous faites une demande à l’un des URI de la ressource, la fonction associée indiquée dans le contrôleur sera exécutée. Chaque fonction dans les fichiers du contrôleur peut utiliser le req et res objets. le commentaire ressource ici est une sous-ressource de Article. Toutes les opérations de requête sont effectuées via le modèle Article afin de rechercher un sous-document et d'effectuer la mise à jour nécessaire. Cependant, chaque fois que vous essayez d'afficher une ressource de commentaire, vous en verrez une même s'il n'y a pas de collection dans MongoDB..
/ articles / 123
(Bien), / articles? id = 123
(Mal).Enfin, si vous concevez une API RESTful en respectant ces règles fondamentales, vous disposerez toujours d'un système flexible, maintenable et facilement compréhensible..