L'API RESTful comprende due concetti principali: Risorsa, e Rappresentazione. La risorsa può essere qualsiasi oggetto associato ai dati o identificato con un URI (più di un URI può fare riferimento alla stessa risorsa) e può essere gestito utilizzando i metodi HTTP. La rappresentazione è il modo in cui visualizzi la risorsa. In questo tutorial tratteremo alcune informazioni teoriche sulla progettazione dell'API RESTful e implementeremo un'API di applicazioni di blogging di esempio utilizzando NodeJS.
La scelta delle risorse corrette per un'API RESTful è una sezione importante della progettazione. Prima di tutto, devi analizzare il tuo dominio aziendale e poi decidere quanti e quali tipi di risorse saranno utilizzati per le tue esigenze aziendali. Se stai progettando un'API di blogging, probabilmente la userai Articolo, Utente, e Commento. Quelli sono i nomi delle risorse e i dati associati sono la risorsa stessa:
"title": "Come progettare RESTful API", "content": "RESTful API design è un caso molto importante nel mondo dello sviluppo software.", "author": "huseyinbabal", "tags": ["tecnologia" , "nodejs", "node-restify"] "category": "NodeJS"
È possibile procedere con un'operazione di risorsa dopo aver deciso le risorse richieste. L'operazione qui si riferisce ai metodi HTTP. Ad esempio, per creare un articolo, puoi effettuare la seguente richiesta:
POST / articoli Host HTTP / 1.1: localhost: 3000 Content-Type: application / json "title": "RESTful API Design con Restify", "slug": "restful-api-design-with-restify", "content" : "Pellentesque habitant morbi tristique senectus et netus et maleuada fames ac turpis egestas.", "Author": "huseyinbabal"
Allo stesso modo, puoi visualizzare un articolo esistente inviando la seguente richiesta:
GET / articles / 123456789012 Host HTTP / 1.1: localhost: 3000 Content-Type: application / json
Che ne pensi di aggiornare un articolo esistente? Posso sentire che stai dicendo:
Posso fare un'altra richiesta POST a / articles / update / 123456789012 con il payload.
Forse preferibile, ma l'URI sta diventando più complesso. Come detto in precedenza, le operazioni possono fare riferimento ai metodi HTTP. Questo significa, dichiarare il aggiornare operazione nel metodo HTTP invece di metterlo nell'URI. Per esempio:
PUT / articoli / 123456789012 Host HTTP / 1.1: localhost: 3000 Content-Type: application / json "title": "Aggiornato come progettare API RESTful", "content": "La progettazione dell'API RESTful aggiornata è un caso molto importante nel software development world. "," author ":" huseyinbabal "," tags ": [" tecnologia "," nodejs "," restify "," un altro tag "]" categoria ":" NodeJS "
A proposito, in questo esempio vedi i tag e i campi delle categorie. Quelli non devono essere campi obbligatori. Puoi lasciarli vuoti e impostarli in futuro.
A volte, è necessario eliminare un articolo quando è obsoleto. In tal caso puoi usare a ELIMINA Richiesta HTTP a / articoli / 123456789012.
I metodi HTTP sono concetti standard. Se li usi come operazione, avrai degli URI semplici e questo tipo di semplice API ti aiuterà a guadagnare consumatori felici.
Cosa succede se si desidera inserire un commento per un articolo? È possibile selezionare l'articolo e aggiungere un nuovo commento all'articolo selezionato. Utilizzando questa dichiarazione, è possibile utilizzare la seguente richiesta:
POST / articoli / 123456789012 / commenti Host HTTP / 1.1: localhost: 3000 Content-Type: application / json "text": "Wow! Questo è un buon tutorial", "author": "john doe"
La precedente forma di risorsa è chiamata come sub-risorsa. Commento è una sotto risorsa di Articolo. Il Commento il carico utile sopra verrà inserito nel database come figlio di Articolo. A volte, un diverso URI si riferisce alla stessa risorsa. Ad esempio, per visualizzare un commento specifico, puoi utilizzare:
GET / articles / 123456789012 / comments / 123 Host HTTP / 1.1: localhost: 3000 Content-Type: application / json
o:
GET / comments / 123456789012 Host HTTP / 1.1: localhost: 3000 Content-Type: application / json
In generale, le funzionalità dell'API cambiano di frequente al fine di fornire nuove funzionalità ai consumatori. In tal caso, possono esistere due versioni della stessa API contemporaneamente. Per separare queste due funzioni, puoi utilizzare il controllo delle versioni. Esistono due forme di controllo delle versioni
/v1.1/articles/123456789012
. GET / articles / 123456789012 Host HTTP / 1.1: localhost: 3000 Accept-Version: 1.0
In realtà, la versione cambia solo la rappresentazione della risorsa, non il concetto della risorsa. Quindi, non è necessario modificare la struttura dell'URI. In v1.1, forse un nuovo campo è stato aggiunto all'articolo. Tuttavia, restituisce ancora un articolo. Nella seconda opzione, l'URI è ancora semplice e gli utenti non hanno bisogno di modificare l'URI nelle implementazioni lato client.
È importante progettare una strategia per situazioni in cui il consumatore non fornisce un numero di versione. È possibile generare un errore quando la versione non viene fornita oppure è possibile restituire una risposta utilizzando la prima versione. Se si utilizza l'ultima versione stabile come predefinita, i consumatori possono ottenere molti errori per le loro implementazioni sul lato client.
La rappresentazione è il modo in cui un'API visualizza la risorsa. Quando chiami un endpoint API, ti verrà restituita una risorsa. Questa risorsa può essere in qualsiasi formato come XML, JSON, ecc. JSON è preferibile se stai progettando una nuova API. Tuttavia, se si aggiorna un'API esistente utilizzata per restituire una risposta XML, è possibile fornire un'altra versione per una risposta JSON.
Sono sufficienti informazioni teoriche sulla progettazione dell'API RESTful. Diamo un'occhiata all'utilizzo della vita reale progettando e implementando un'API di Blogging usando Restify.
Per progettare un'API RESTful, dobbiamo analizzare il dominio aziendale. Quindi possiamo definire le nostre risorse. In un'API di Blogging, abbiamo bisogno di:
In questa API, non illustrerò come autenticare un utente per creare un articolo o un commento. Per la parte di autenticazione, è possibile fare riferimento all'esercitazione basata su token con AngularJS & NodeJS tutorial.
I nostri nomi delle risorse sono pronti. Le operazioni sulle risorse sono semplicemente CRUD. È possibile fare riferimento alla seguente tabella per una presentazione generale dell'API.
Nome della risorsa | Verbi HTTP | Metodi HTTP |
---|---|---|
Articolo | creare articolo aggiorna l'articolo elimina l'articolo vedi articolo | POST / articoli con payload PUT / articoli / 123 con Payload CANCELLA / articoli / 123 GET / article / 123 |
Commento | crea un commento aggiorna Coment elimina commento vedi Commento | POST / articoli / 123 / commenti con payload PUT / comments / 123 con Payload CANCELLA / commenti / 123 OTTIENI / commenti / 123 |
Utente | creare un utente aggiorna utente cancella utente guarda Utente | POST / utenti con payload PUT / users / 123 con Payload ELIMINA / utenti / 123 GET / users / 123 |
In questo progetto useremo NodeJS con Restify. Le risorse saranno salvate nel MongoDB Banca dati. Prima di tutto, possiamo definire le risorse come modelli in Restify.
var mongoose = require ("mangusta"); var Schema = mongoose.Schema; var ArticleSchema = new Schema (title: String, slug: String, content: String, author: type: String, ref: "User"); mongoose.model ('Article', ArticleSchema);
var mongoose = require ("mangusta"); var Schema = mongoose.Schema; var CommentSchema = new Schema (text: String, article: type: String, ref: "Article", author: type: String, ref: "User"); mongoose.model ('Comment', CommentSchema);
Non ci sarà alcuna operazione per la risorsa Utente. Assumeremo che conosciamo già l'utente corrente che sarà in grado di operare su articoli o commenti.
Puoi chiedere da dove viene questo modulo mangusta. È il framework ORM più popolare per MongoDB scritto come modulo NodeJS. Questo modulo è incluso nel progetto all'interno di un altro file di configurazione.
Ora possiamo definire i nostri verbi HTTP per le risorse di cui sopra. Puoi vedere quanto segue:
var restify = require ('restify'), fs = require ('fs') var controller = , controllers_path = process.cwd () + '/ app / controller' fs.readdirSync (controllers_path) .forEach (funzione (file ) if (file.indexOf ('. js')! = -1) controller [file.split ('.') [0]] = richiede (percorso_controls + '/' + file)) var server = restify.createServer (); server .use (restify.fullResponse ()) .use (restify.bodyParser ()) // Article Avvia server.post ("/ articles", controllers.article.createArticle) server.put ("/ articles /: id", controller.article.updateArticle) server.del ("/ articles /: id", controllers.article.deleteArticle) server.get (percorso: "/ articles /: id", versione: "1.0.0", controller. article.viewArticle) server.get (percorso: "/ articles /: id", versione: "2.0.0", controllers.article.viewArticle_v2) // Articolo Fine // Commento Avvia server.post ("/ comments" , controllers.comment.createComment) server.put ("/ commenti /: id", controllers.comment.viewComment) server.del ("/ commenti /: id", controllers.comment.deleteComment) server.get ("/ comments /: id ", controllers.comment.viewComment) // Comment End var port = process.env.PORT || 3000; server.listen (port, function (err) if (err) console.error (err) else console.log ('L'app è pronta a:' + port)) if (process.env.environment == 'production') ) process.on ('uncaughtException', function (err) console.error (JSON.parse (JSON.stringify (err, ['stack', 'message', 'inner'], 2))))
In questo frammento di codice, prima di tutto i file controller che contengono i metodi controller vengono iterati e tutti i controller sono inizializzati per eseguire una richiesta specifica all'URI. Successivamente, gli URI per operazioni specifiche sono definiti per le operazioni CRUD di base. C'è anche il controllo delle versioni per una delle operazioni sull'articolo.
Ad esempio, se si specifica la versione come 2
nell'intestazione Accept-Version, viewArticle_v2
sarà eseguito. viewArticle
e viewArticle_v2
entrambi fanno lo stesso lavoro, mostrando la risorsa, ma mostrano la risorsa articolo in un formato diverso, come puoi vedere nel titolo
campo sottostante. Infine, il server viene avviato su una porta specifica e vengono applicati alcuni controlli di segnalazione degli errori. Possiamo procedere con i metodi del controller per le operazioni HTTP sulle risorse.
var mongoose = require ('mangusta'), Article = mongoose.model ("Articolo"), 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: "Errore occorso:" + 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 (tipo: falso, dati: "Errore occorso:" + err) else if (articolo) res.json (tipo: true, dati: articolo ) else res.json (type: false, data: "Articolo:" + req.params.id + "non trovato") export.viewArticle_v2 = function (req, res, next) Article.findById (new ObjectId (req.params.id), function (err, article) if (err) res.status (500); res.json (type: false, data: "Errore occorso:" + err) else if (article) article.title = article.title + "v2" res.json (tipo: vero, dati: articolo) else res.json (tipo: falso, dati : "Articolo:" + req.params.id + "non trovato")) exports.updateArticle = function (req, res, next) var updatedArticleModel = new Article (req.body); Article.findByIdAndUpdate (new ObjectId (req.params.id), updatedArticleModel, function (err, article) if (err) res.status (500); res.json (type: false, data: "Errore occorso: "+ err) else if (article) res.json (type: true, data: article) else res.json (type: false, data:" Articolo: "+ req.params. id + "not found")) exports.deleteArticle = function (req, res, next) Article.findByIdAndRemove (new Object (req.params.id), function (err, article) if (err ) res.status (500); res.json (tipo: falso, dati: "Errore occorso:" + err) else res.json (tipo: vero, dati: "Articolo:" + req. params.id + "cancellato con successo"))
Puoi trovare una spiegazione delle operazioni CRUD di base sul lato Mongoose qui sotto:
articleModel
inviato dal corpo della richiesta. Un nuovo modello può essere creato passando il corpo della richiesta come costruttore a un modello come var articleModel = new Article (req.body)
. trova uno
con un parametro ID è sufficiente per restituire i dettagli dell'articolo.salvare
comando.findByIdAndRemove
è il modo migliore per eliminare un articolo fornendo l'ID dell'articolo.I comandi di Mongoose menzionati sopra sono semplicemente metodi statici come l'oggetto Article che è anche un riferimento allo schema di Mongoose.
var mongoose = require ('mangusta'), Comment = mongoose.model ("Comment"), Article = mongoose.model ("Article"), ObjectId = mongoose.Types.ObjectId exports.viewComment = function (req, res) Article.findOne ("comments._id": nuovo ObjectId (req.params.id), "commenti. $": 1, funzione (err, commento) if (err) res.status (500) ; res.json (type: false, data: "Errore occorso:" + err) else if (commento) res.json (tipo: vero, dati: nuovo Commento (comment.comments [0]) ) else res.json (tipo: falso, dati: "Commento:" + req.params.id + "non trovato") exports.updateComment = function (req, res, next) var updatedCommentModel = new Comment (req.body); console.log (updatedCommentModel) Article.update ("comments._id": nuovo ObjectId (req.params.id), "$ set": "commenti. $. testo": updatedCommentModel.text, "commenti. $ .author ": updatedCommentModel.author, function (err) if (err) res.status (500); res.json (type: false, data:" Errore occorso: "+ err) else res.json (type: true, data: "Commento:" + req.params.id + "updated")) exports.deleteComment = function (req, res, next) Article.findOneAndUpdate ( "comments._id": nuovo ObjectId (req.params.id), "$ pull": "commenti": "_id": nuovo ObjectId (req.params.id), funzione (err, articolo) if (err) res.status (500); res.json (type: false, data: "Errore occorso:" + err) else if (article) res.json (type: true, data: article) else res.json (type: false, data: "Commento:" + req.params.id + "not found")
Quando si effettua una richiesta a uno degli URI delle risorse, verrà eseguita la relativa funzione indicata nel controller. Ogni funzione all'interno dei file del controller può utilizzare req e res oggetti. Il commento la risorsa qui è una sotto-risorsa di Articolo. Tutte le operazioni di interrogazione vengono effettuate tramite il modello Articolo per trovare un sottodocumento e apportare l'aggiornamento necessario. Tuttavia, ogni volta che provi a visualizzare una risorsa di commento, ne vedrai una anche se non ci sono raccolte in MongoDB.
/ articoli / 123
(Buono), / Articoli? Id = 123
(Male).Infine, se si progetta un'API RESTful seguendo queste regole fondamentali, si avrà sempre un sistema flessibile, manutenibile e facilmente comprensibile.