RESTful API består av två huvud begrepp: Resurs, och Representation. Resurs kan vara något objekt som är associerat med data eller identifieras med en URI (mer än en URI kan referera till samma resurs) och kan användas med HTTP-metoder. Representation är hur du visar resursen. I den här handledningen kommer vi att täcka några teoretiska uppgifter om RESTful API-design och implementera ett exempel på API för blogging med NodeJS.
Att välja rätt resurser för ett RESTful API är en viktig del av designen. Först och främst måste du analysera din företagsdomän och bestämma sedan hur många och vilka resurser som ska användas som är relevanta för ditt företags behov. Om du utformar ett bloggprogram, använder du förmodligen Artikel, Användare, och Kommentar. Det är resursnamnen, och de uppgifter som är förknippade med det är själva resursen:
"title": "Hur man utformar RESTful API", "content": "RESTful API-design är ett mycket viktigt fall i programvaruutvecklingsvärlden.", "Författare": "huseyinbabal", "tags": ["technology" , "nodejs", "node-restify"] "category": "NodeJS"
Du kan fortsätta med en resursoperation när du har bestämt dig för de nödvändiga resurserna. Drift här hänvisar till HTTP-metoder. För att till exempel skapa en artikel kan du göra följande förfrågan:
POST / artiklar HTTP / 1.1 Host: localhost: 3000 Innehållstyp: application / json "title": "RESTful API Design with Restify", "slug": "restful-api-design-with-restify" : "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", "Författare": "huseyinbabal"
På samma sätt kan du se en befintlig artikel genom att utfärda följande förfrågan:
GET / articles / 123456789012 HTTP / 1.1 Värd: localhost: 3000 Innehållstyp: application / json
Vad sägs om att uppdatera en befintlig artikel? Jag kan höra att du säger:
Jag kan göra en annan POST-förfrågan till / artiklar / uppdatering / 123456789012 med nyttolasten.
Kanske föredraget, men URI blir alltmer komplex. Som vi tidigare sagt kan operationer hänvisa till HTTP-metoder. Detta betyder, ange uppdatering operation i HTTP-metoden istället för att sätta det i URI. Till exempel:
PUT / articles / 123456789012 HTTP / 1.1 Värd: localhost: 3000 Innehållstyp: application / json "title": "Uppdaterad hur man utformar RESTful API", "content": "Uppdaterad RESTful API-design är ett mycket viktigt fall i mjukvaruutvecklingsvärld. "," författare ":" huseyinbabal "," tags ": [" technology "," nodejs "," restify "," one more tag "]" category ":" NodeJS "
Förresten, i det här exemplet ser du taggar och kategorifält. De behöver inte vara obligatoriska fält. Du kan lämna dem tomma och ställa in dem i framtiden.
Ibland måste du radera en artikel när den är föråldrad. I så fall kan du använda en RADERA HTTP-begäran till / artiklar / 123456789012.
HTTP-metoder är standardkoncept. Om du använder dem som en operation har du enkla URI-filer, och den här typen av enkelt API hjälper dig att få glada konsumenter.
Vad händer om du vill lägga in en kommentar till en artikel? Du kan välja artikeln och lägga till en ny kommentar till den valda artikeln. Genom att använda detta uttalande kan du använda följande förfrågan:
POST / articles / 123456789012 / comments HTTP / 1.1 Värd: localhost: 3000 Innehållstyp: application / json "text": "Wow! Detta är en bra handledning", "författare": "John Doe"
Ovanstående resursform kallas som en sub-resurs. Kommentar är en delresurs av Artikel. De Kommentar nyttolast ovan kommer att införas i databasen som ett barn av Artikel. Ibland avser en annan URI samma resurs. Om du till exempel vill visa en specifik kommentar kan du använda antingen:
GET / articles / 123456789012 / comments / 123 HTTP / 1.1 Värd: localhost: 3000 Innehållstyp: application / json
eller:
GET / comments / 123456789012 HTTP / 1.1 Värd: localhost: 3000 Innehållstyp: application / json
I allmänhet ändras API-funktionerna ofta för att ge nya funktioner till konsumenterna. I det fallet kan två versioner av samma API existera samtidigt. För att skilja dessa två funktioner, kan du använda versioning. Det finns två former av versionering
/v1.1/articles/123456789012
. GET / articles / 123456789012 HTTP / 1.1 Värd: localhost: 3000 Accept-Version: 1.0
I själva verket ändras versionen endast av resursens representation, inte resursens begrepp. Så, du behöver inte ändra URI-strukturen. I v1.1, kanske ett nytt fält lagts till i artikeln. Det returnerar dock fortfarande en artikel. I det andra alternativet är URI fortfarande enkelt och konsumenter behöver inte ändra sina URI på klientsidan implementeringar.
Det är viktigt att utforma en strategi för situationer där konsumenten inte tillhandahåller ett versionsnummer. Du kan hämta ett fel när version inte finns, eller du kan returnera ett svar med den första versionen. Om du använder den senaste stabila versionen som standard kan konsumenterna få många fel för sina implementeringar på klienten.
Representation är hur ett API visar resursen. När du ringer till en API-slutpunkt kommer du att få tillbaka en resurs. Denna resurs kan vara i något format som XML, JSON, etc. JSON är att föredra om du utformar ett nytt API. Men om du uppdaterar ett befintligt API som används för att returnera ett XML-svar, kan du tillhandahålla en annan version för ett JSON-svar.
Det finns tillräckligt med teoretisk information om RESTful API-design. Låt oss ta en titt på verkligheten genom att utforma och implementera ett Blogging API med hjälp av Restify.
För att kunna designa ett RESTful API måste vi analysera affärsdomen. Då kan vi definiera våra resurser. I ett Blogging API behöver vi:
I detta API täcker jag inte hur jag autentiserar en användare för att skapa en artikel eller kommentar. För autentiseringsdelen kan du referera till Tokenbaserad autentisering med AngularJS & NodeJS handledning.
Våra resursnamn är redo. Resursverksamheten är helt enkelt CRUD. Du kan referera till följande tabell för en allmän presentation av API.
Resursnamn | HTTP Verbs | HTTP-metoder |
---|---|---|
Artikel | skapa artikel uppdatera artikeln ta bort artikel visa artikel | POST / artiklar med nyttolast PUT / artiklar / 123 med nyttolast DELETE / articles / 123 GET / artikel / 123 |
Kommentar | skapa kommentar uppdatera kommentaren ta bort kommentar visa kommentar | POST / artiklar / 123 / kommentarer med nyttolast PUT / kommentarer / 123 med nyttolast DELETE / comments / 123 GET / comments / 123 |
Användare | skapa användare uppdatera användaren Ta bort Användare visa användare | POST / användare med nyttolast PUT / användare / 123 med nyttolast DELETE / users / 123 GET / users / 123 |
I det här projektet kommer vi att använda NodeJS med Restify. Resurserna sparas i MongoDB databas. Först av allt kan vi definiera resurser som modeller i Restify.
var mongoose = kräver ("mongoose"); var Schema = mongoose. Schema; var ArticleSchema = nytt schema (title: String, slug: String, innehåll: String, författare: typ: String, ref: "User"); mongoose.model ("Article", ArticleSchema);
var mongoose = kräver ("mongoose"); var Schema = mongoose. Schema; var CommentSchema = nytt schema (text: String, artikel: typ: String, ref: "Article", författare: typ: String, ref: "User")); mongoose.model ('Comment', CommentSchema);
Det finns ingen operation för användarresursen. Vi antar att vi redan vet den nuvarande användaren som kommer att kunna arbeta på artiklar eller kommentarer.
Du kan fråga var denna mongoosmodul kommer ifrån. Det är den mest populära ORM-ramen för MongoDB skrivet som en NodeJS-modul. Den här modulen ingår i projektet inom en annan config-fil.
Nu kan vi definiera våra HTTP-verb för ovanstående resurser. Du kan se följande:
var restify = require ('restify'), fs = kräver ('fs') var controllers = , controllers_path = process.cwd () + '/ app / controllers'fs.readdirSync (controllers_path) .forEach (funktion ) if (file.indexOf ('. js')! = -1) controllers [file.split ('.') [0]] = kräver (controllers_path + '/' + fil)) var server = restify.createServer (); server .use (restify.fullResponse ()) .use (restify.bodyParser ()) // Artikel Start server.post ("/ articles", controllers.article.createArticle) server.put ("/ articles /: id" controllers.article.updateArticle) server.del ("/ articles /: id", controllers.article.deleteArticle) server.get (path: "/ articles /: id", version: "1.0.0", controllers. article.viewArticle) server.get (path: "/ articles /: id", version: "2.0.0", controllers.article.viewArticle_v2) // Artikel Slut // Kommentar Start server.post ("/ comments" , controllers.comment.createComment) server.put ("/ kommentarer /: id", controllers.comment.viewComment) server.del ("/ kommentarer /: id", controllers.comment.deleteComment) server.get ("/ kommentarer /: id ", controllers.comment.viewComment) // Kommentar Slut var port = process.env.PORT || 3000; server.listen (port, funktion (err) om (err) console.error (err) else console.log ('App är klar till:' + port)) om (process.env.environment == 'production' ) process.on ('uncaughtException', funktion (err) console.error (JSON.parse (JSON.stringify (err, ['stack', 'message', 'inner'], 2)))))
I det här kodbiten, först och främst kontrollerfilerna som innehåller regleringsmetoder itereras och alla styrenheter initieras för att utföra en specifik begäran till URI. Därefter definieras URI för specifika operationer för grundläggande CRUD-operationer. Det finns också versioning för en av transaktionerna på artikeln.
Till exempel, om du anger version som 2
i Accept-Version header, viewArticle_v2
kommer att utföras. viewArticle
och viewArticle_v2
båda gör samma jobb och visar resursen, men de visar artikelresurs i ett annat format, som du kan se i titel
fält nedan. Slutligen startas servern på en viss port, och vissa felrapporteringskontroller tillämpas. Vi kan fortsätta med kontrollmetoder för HTTP-operationer på resurser.
var mongoose = kräver ('mongoose'), Artikel = mongoose.model ("Artikel"), ObjectId = mongoose.Types.ObjectId exports.createArticle = funktion (req, res, nästa) var articleModel = ny artikel (req.body ); articleModel.save (funktion (fel, artikel) om (err) res.status (500); res.json (typ: false, data: "Fel uppstod:" + err) else res.json type: true, data: article)) exports.viewArticle = funktion (req, res, nästa) Article.findById (new ObjectId (req.params.id) err) res.status (500); res.json (typ: false, data: "Fel uppstod:" + err) annat om (artikel) res.json (typ: true, data: article annat res.json (typ: falskt, data: "Artikel:" + req.params.id + "not found")) exports.viewArticle_v2 = funktion (req, res, nästa) Article.findById (new ObjectId (req.params.id), funktion (err, artikel) om (err) res.status (500); res.json (typ: false, data: "Fel uppstod:" + err) annat om (artikel) article.title = article.title + "v2" res.json (typ: true, data: article) annat res.json : "Artikel:" + req.params.id + "not found")) exports.updateArticle = funktion (req, res, nästa) var updatedArticleModel = ny artikel (req.body); Article.findByIdAndUpdate (new ObjectId (req.params.id), updatedArticleModel, funktion (err, artikel) om (err) res.status (500); res.json (typ: false, data: "Fel uppstod: "+ err) annat om (artikel) res.json (typ: true, data: article) else res.json (typ: false, data:" Article: "+ req.params. id = "inte hittat") exports.deleteArticle = funktion (req, res, nästa) Article.findByIdAndRemove (nytt objekt (req.params.id) ) res.json (typ: false, data: "Fel uppstod:" + err) annat res.json (typ: true, data: "Artikel:" + req. params.id + "raderas framgångsrikt"))
Du kan hitta en förklaring av grundläggande CRUD-operationer på Mongoose-sidan nedan:
articleModel
skickat från begäran organ. En ny modell kan skapas genom att överföra begäran kroppen som en konstruktör till en modell som var articleModel = ny artikel (req.body)
. hitta en
med en ID-parameter är tillräckligt för att returnera artikeldetaljer.spara
kommando.findByIdAndRemove
är det bästa sättet att ta bort en artikel genom att tillhandahålla artikel-id.De ovan nämnda mongooskommandon är helt enkelt statiska som metod genom artikelobjekt som också är en hänvisning till mongooschemat.
var mongoose = kräver ('mongoose'), Kommentar = mongoose.model ("Kommentar"), Artikel = mongoose.model ("Artikel"), ObjectId = mongoose.Types.ObjectId exports.viewComment = funktion (req, res) Article.findOne ("comments._id": new ObjectId (req.params.id), "kommentarer. $": 1, funktion (fel, kommentar) om (err) res.status (500) ; res.json (typ: false, data: "Fel uppstod:" + err) annat om (kommentar) res.json (typ: true, data: new Kommentar (comment.comments [0]) res.json (typ: falskt, data: "Kommentar:" + req.params.id + "not found")) exports.updateComment = funktion (req, res, nästa) var updatedCommentModel = ny kommentar (req.body); console.log (updatedCommentModel) Article.update ("comments._id": nytt ObjectId (req.params.id), "$ set": "kommentarer. $. text": updatedCommentModel.text, "kommentarer. $ .author ": updatedCommentModel.author, funktion (err) om (err) res.status (500); res.json (typ: false, data:" Fel uppstod: "+ err) res.json (typ: true, data: "Kommentar:" + req.params.id + "updated")) exports.deleteComment = funktion (req, res, nästa) Article.findOneAndUpdate "comments._id": new ObjectId (req.params.id), "$ pull": "kommentarer": "_id": nytt ObjectId (req.params.id), funktionen (fel, artikeln if (err) res.status (500); res.json (typ: false, data: "Fel uppstod:" + err) annat if (article) res.json sant, data: artikel) annat res.json (typ: false, data: "Kommentar:" + req.params.id + "not found"))
När du gör en förfrågan till en av resurs-URI: erna, kommer den relaterade funktionen som anges i regulatorn att exekveras. Varje funktion i kontrollerfilerna kan använda req och res objekt. De kommentar resursen här är en delresurs av Artikel. Alla frågeoperationer görs genom artikelmodellen för att hitta ett underdokument och göra den nödvändiga uppdateringen. Men när du försöker visa en kommentarresurs ser du en även om det inte finns någon samling i MongoDB.
/ artiklar / 123
(Bra), / Artiklar? Id = 123
(Dålig).Slutligen, om du utformar ett RESTful API genom att följa dessa grundläggande regler, kommer du alltid att ha ett flexibelt, underhållbart, lättförståeligt system.