Rumah > Artikel > hujung hadapan web > Mereka bentuk API RESTful menggunakan NodeJS dan Restify
RESTful API terdiri daripada dua konsep utama: Sumber dan Perwakilan. Sumber boleh menjadi sebarang objek yang dikaitkan dengan data, atau dikenal pasti oleh URI (berbilang URI boleh merujuk sumber yang sama), dan boleh dimanipulasi menggunakan kaedah HTTP. Perwakilan ialah cara sumber dipaparkan. Dalam tutorial ini, kami akan merangkumi beberapa maklumat teori tentang reka bentuk API RESTful dan melaksanakan contoh API aplikasi blog menggunakan NodeJS.
Memilih sumber yang sesuai untuk API RESTful adalah bahagian penting dalam reka bentuk. Mula-mula, anda perlu menganalisis domain perniagaan anda dan kemudian memutuskan amaun dan jenis sumber untuk digunakan yang berkaitan dengan keperluan perniagaan anda. Jika anda mereka bentuk API blog, anda mungkin menggunakan Siaran, Pengguna dan Ulasan. Ini ialah nama sumber dan data yang dikaitkan dengannya ialah sumber itu sendiri:
{ "title": "How to Design RESTful API", "content": "RESTful API design is a very important case in the software development world.", "author": "huseyinbabal", "tags": [ "technology", "nodejs", "node-restify" ] "category": "NodeJS" }
Selepas mengenal pasti sumber yang anda perlukan, anda boleh meneruskan operasi sumber. Operasi di sini merujuk kepada kaedah HTTP. Contohnya, untuk membuat artikel, anda boleh membuat permintaan berikut:
POST /articles HTTP/1.1 Host: localhost:3000 Content-Type: application/json { "title": "RESTful API Design with Restify", "slug": "restful-api-design-with-restify", "content": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", "author": "huseyinbabal" }
Dengan cara yang sama, anda boleh melihat artikel sedia ada dengan membuat permintaan berikut:
GET /articles/123456789012 HTTP/1.1 Host: localhost:3000 Content-Type: application/json
Bagaimana dengan mengemas kini artikel sedia ada? Saya mendengar anda berkata:
Saya boleh membuat permintaan POST lagi ke /articles/update/123456789012 dengan muatan.
Mungkin lebih baik, tetapi URI semakin kompleks. Seperti yang kami katakan sebelum ini, operasi boleh merujuk kaedah HTTP. Ini bermakna, mengisytiharkan operasi kemas kini dalam kaedah HTTP dan bukannya meletakkannya dalam URI. Contohnya:
PUT /articles/123456789012 HTTP/1.1 Host: localhost:3000 Content-Type: application/json { "title": "Updated How to Design RESTful API", "content": "Updated RESTful API design is a very important case in the software development world.", "author": "huseyinbabal", "tags": [ "technology", "nodejs", "restify", "one more tag" ] "category": "NodeJS" }
Dengan cara ini, dalam contoh ini anda akan melihat medan teg dan kategori. Ini tidak perlu menjadi medan yang diperlukan. Anda boleh membiarkannya kosong dan menetapkannya pada masa hadapan.
Kadangkala, anda perlu memadamkan artikel apabila ia sudah lapuk. Dalam kes ini, anda boleh menggunakan permintaan DELETEHTTP untuk /articles/123456789012.
Kaedah HTTP adalah konsep standard. Jika anda menggunakannya sebagai tindakan, anda akan mempunyai URI yang mudah dan API mudah ini akan membantu anda memperoleh pengguna yang gembira.
Bagaimana jika anda ingin memasukkan ulasan ke dalam artikel anda? Anda boleh memilih artikel dan menambah ulasan baharu pada artikel yang dipilih. Dengan menggunakan pernyataan ini anda boleh menggunakan permintaan berikut:
POST /articles/123456789012/comments HTTP/1.1 Host: localhost:3000 Content-Type: application/json { "text": "Wow! this is a good tutorial", "author": "john doe" }
Sumber dalam bentuk di atas dipanggil sub-sumber. Komen ialah sub-sumber artikel. Muatan Komendi atas akan dimasukkan ke dalam pangkalan data sebagai anak kepada Artikel. Kadangkala, URI yang berbeza merujuk kepada sumber yang sama. Sebagai contoh, untuk melihat ulasan tertentu anda boleh menggunakan:
GET /articles/123456789012/comments/123 HTTP/1.1 Host: localhost:3000 Content-Type: application/json
atau:
GET /comments/123456789012 HTTP/1.1 Host: localhost:3000 Content-Type: application/json
Secara amnya, fungsi API sering berubah untuk menyediakan fungsi baharu kepada pengguna. Dalam kes ini, dua versi API yang sama boleh wujud serentak. Untuk memisahkan kedua-dua fungsi ini, anda boleh menggunakan kawalan versi. Kawalan versi datang dalam dua bentuk
/v1.1/articles/123456789012
.
GET /articles/123456789012 HTTP/1.1 Host: localhost:3000 Accept-Version: 1.0
Malah, versi hanya mengubah perwakilan sumber, bukan konsep sumber. Oleh itu, anda tidak perlu menukar struktur URI. Dalam v1.1, medan baharu mungkin telah ditambahkan dalam Artikel. Walau bagaimanapun, ia masih mengembalikan artikel. Dalam pilihan kedua, URI kekal mudah dan pengguna tidak perlu menukar URInya dalam pelaksanaan klien.
Sangat penting untuk mereka bentuk strategi untuk situasi di mana pengguna tidak memberikan nombor versi. Anda boleh menimbulkan ralat apabila tiada versi disediakan atau mengembalikan respons dengan versi pertama. Jika anda menggunakan versi stabil terkini sebagai versi lalai, pelaksanaan pelanggan pengguna mungkin mempunyai banyak pepijat.
mewakili cara API memaparkan sumber. Apabila anda memanggil titik akhir API, anda mengembalikan sumber. Sumber boleh dalam sebarang format seperti XML, JSON, dsb. Jika anda mereka bentuk API baharu, sebaiknya gunakan JSON. Walau bagaimanapun, jika anda mengemas kini API sedia ada yang mengembalikan respons XML, anda boleh menyediakan versi lain untuk respons JSON.
Maklumat teori yang mencukupi tentang reka bentuk API RESTful. Mari lihat penggunaan dalam kehidupan sebenar dengan mereka bentuk dan melaksanakan API blog menggunakan Restify.
Untuk mereka bentuk API RESTful, kami perlu menganalisis domain perniagaan. Kemudian kita boleh menentukan sumber kita. Dalam Blog API kita perlukan:
khusus
Dalam API ini saya tidak akan membincangkan cara mengesahkan pengguna untuk membuat artikel atau ulasan. Untuk bahagian pengesahan, anda boleh rujuk Tutorial Pengesahan Berasaskan Token untuk AngularJS & NodeJS.
Nama sumber kami sedia. Operasi sumber adalah CRUD mudah. Anda boleh merujuk kepada jadual di bawah untuk mendapatkan gambaran keseluruhan API.
Nama sumber | Kata kerja HTTP | Kaedah HTTP |
---|---|---|
Artikel | Buat artikel Kemas kini artikel Padam artikel Lihat artikel |
POST /artikel dengan Muatan LETAKKAN /artikel/123 dengan Muatan DELETE /articles/123 DAPATKAN /article/123 |
Komen | Buat ulasan Kemas kini ulasan Padam ulasan Lihat ulasan
|
POST /articles/123/komen dengan muatan LETAK /komen/123 dengan muatan PADAM /komen/123 DAPATKAN /komen/123 |
Pengguna | Buat pengguna Kemas kini pengguna Padam pengguna Lihat pengguna |
POST /pengguna dengan Muatan LETAKKAN /pengguna/123 dengan Muatan PADAM /pengguna/123 DAPATKAN /pengguna/123 |
在此项目中,我们将使用 NodeJS 和 Restify。资源将保存在 MongoDB 数据库中。首先,我们可以在Restify中将资源定义为模型。
var mongoose = require("mongoose"); 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("mongoose"); var Schema = mongoose.Schema; var CommentSchema = new Schema({ text: String, article: { type: String, ref: "Article" }, author: { type: String, ref: "User" } }); mongoose.model('Comment', CommentSchema);
不会对用户资源进行任何操作。我们假设我们已经知道能够对文章或评论进行操作的当前用户。
您可能会问这个猫鼬模块来自哪里。它是作为 NodeJS 模块编写的最流行的 MongoDB ORM 框架。该模块包含在项目的另一个配置文件中。
现在我们可以为上述资源定义 HTTP 动词。您可以看到以下内容:
var restify = require('restify') , fs = require('fs') var controllers = {} , controllers_path = process.cwd() + '/app/controllers' fs.readdirSync(controllers_path).forEach(function (file) { 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({path: "/articles/:id", version: "1.0.0"}, controllers.article.viewArticle) server.get({path: "/articles/:id", version: "2.0.0"}, controllers.article.viewArticle_v2) // Article End // Comment Start 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(port, function (err) { if (err) console.error(err) else console.log('App is ready at : ' + port) }) if (process.env.environment == 'production') process.on('uncaughtException', function (err) { console.error(JSON.parse(JSON.stringify(err, ['stack', 'message', 'inner'], 2))) })
在此代码片段中,首先迭代包含控制器方法的所有控制器文件,并初始化所有控制器,以便执行对 URI 的特定请求。之后,为基本的CRUD操作定义了具体操作的URI。 Article 上的其中一项操作也有版本控制。
例如,如果您在 Accept-Version 标头中将版本声明为 2
,则将执行 viewArticle_v2
。 viewArticle
和 viewArticle_v2
都执行相同的工作,显示资源,但它们以不同的格式显示文章资源,正如您在 中看到的那样title
字段如下。最后,服务器在特定端口上启动,并应用一些错误报告检查。我们可以继续使用控制器方法对资源进行 HTTP 操作。
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: "Error occured: " + 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: "Error occured: " + err }) } else { if (article) { res.json({ type: true, data: article }) } else { res.json({ type: false, data: "Article: " + req.params.id + " not found" }) } } }) } exports.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: "Error occured: " + err }) } else { if (article) { article.title = article.title + " v2" res.json({ type: true, data: article }) } else { res.json({ type: false, data: "Article: " + req.params.id + " not found" }) } } }) } 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: "Error occured: " + err }) } else { if (article) { res.json({ type: true, data: article }) } else { res.json({ type: false, data: "Article: " + 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({ type: false, data: "Error occured: " + err }) } else { res.json({ type: true, data: "Article: " + req.params.id + " deleted successfully" }) } }) }
您可以在下面找到 Mongoose 端基本 CRUD 操作的说明:
articleModel
的简单保存操作。可以通过将请求正文作为构造函数传递给模型来创建新模型,例如 vararticleModel = new Article(req.body)
。 findOne
带有 ID 参数足以返回文章详细信息。save
命令将更新后的模型保存到数据库中。findByIdAndRemove
是通过提供文章 ID 来删除文章的最佳方法。上面提到的 Mongoose 命令只是通过 Article 对象进行静态方法,该对象也是 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": new ObjectId(req.params.id)}, {"comments.$": 1}, function(err, comment) { if (err) { res.status(500); res.json({ type: false, data: "Error occured: " + err }) } else { if (comment) { res.json({ type: true, data: new Comment(comment.comments[0]) }) } else { res.json({ type: false, data: "Comment: " + req.params.id + " not found" }) } } }) } 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: "Error occured: " + err }) } else { res.json({ type: true, data: "Comment: " + req.params.id + " updated" }) } }) } exports.deleteComment = function(req, res, next) { 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: "Error occured: " + err }) } else { if (article) { res.json({ type: true, data: article }) } else { res.json({ type: false, data: "Comment: " + req.params.id + " not found" }) } } }) }
当您向某个资源 URI 发出请求时,控制器中声明的相关函数将被执行。控制器文件中的每个函数都可以使用 req 和 res 对象。这里的评论资源是文章的子资源。 所有的查询操作都是通过Article模型进行的,以便找到子文档并进行必要的更新。但是,每当您尝试查看 Comment 资源时,即使 MongoDB 中没有集合,您也会看到一个 Comment 资源。
/articles/123
(好),/articles?id=123
(差)。最后,如果您按照这些基本规则设计 RESTful API,您将始终拥有一个灵活、可维护、易于理解的系统。
Atas ialah kandungan terperinci Mereka bentuk API RESTful menggunakan NodeJS dan Restify. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!