RESTful API는 Resources 및 Representations라는 두 가지 주요 개념으로 구성됩니다. 리소스는 데이터와 연결되거나 URI로 식별되는 모든 개체일 수 있으며(여러 URI가 동일한 리소스를 참조할 수 있음) HTTP 메서드를 사용하여 조작할 수 있습니다. 표현은 리소스가 표시되는 방식입니다. 이 튜토리얼에서는 RESTful API 디자인에 대한 몇 가지 이론적 정보를 다루고 NodeJS를 사용하여 샘플 블로그 애플리케이션 API를 구현합니다.
RESTful API에 적합한 리소스를 선택하는 것은 디자인의 중요한 부분입니다. 먼저 비즈니스 도메인을 분석한 다음 비즈니스 요구 사항과 관련하여 사용할 리소스의 양과 유형을 결정해야 합니다. 블로그 API를 설계하는 경우 Posts, Users 및 Comments를 사용할 수 있습니다. 다음은 리소스 이름이며 이와 관련된 데이터는 리소스 자체입니다.
으아아아필요한 자원을 파악한 후 자원 운영을 진행할 수 있습니다. 여기서의 작업은 HTTP 메서드를 나타냅니다. 예를 들어 기사를 작성하려면 다음 요청을 할 수 있습니다.
으아아아동일하게 다음 요청을 하면 기존 기사를 볼 수 있습니다.
으아아아기존 기사를 업데이트하는 것은 어떻습니까? 나는 당신이 말하는 것을 들었습니다:
페이로드를 사용하여 /articles/update/123456789012에 또 다른 POST 요청을 할 수 있습니다.
더 좋을 수도 있지만 URI가 점점 더 복잡해지고 있습니다. 앞서 말했듯이 작업은 HTTP 메서드를 참조할 수 있습니다. 즉, update 작업을 URI에 넣는 대신 HTTP 메서드에서 선언하는 것입니다. 예:
으아아아그런데 이 예에서는 태그 및 카테고리 필드가 표시됩니다. 필수 필드일 필요는 없습니다. 공백으로 두고 나중에 설정할 수 있습니다.
기사가 오래되면 삭제해야 하는 경우도 있습니다. 이 경우 /articles/123456789012에 대한 DELETEHTTP 요청을 사용할 수 있습니다.
HTTP 방법은 표준 개념입니다. 이를 작업으로 사용하면 간단한 URI를 갖게 되며 이 간단한 API는 행복한 소비자를 얻는 데 도움이 됩니다.
글에 댓글을 삽입하고 싶다면 어떻게 해야 하나요? 기사를 선택하고 선택한 기사에 새 댓글을 추가할 수 있습니다. 이 문을 사용하면 다음 요청을 사용할 수 있습니다.
으아아아위 형태의 리소스를 하위 리소스라고 합니다. 댓글은 기사의 하위 리소스입니다. 위의 Comments 페이로드는 Articles의 하위 항목으로 데이터베이스에 삽입됩니다. 때로는 서로 다른 URI가 동일한 리소스를 참조하는 경우도 있습니다. 예를 들어 특정 댓글을 보려면 다음을 사용할 수 있습니다.
으아아아또는:
으아아아일반적으로 API 기능은 소비자에게 새로운 기능을 제공하기 위해 자주 변경됩니다. 이 경우 동일한 API의 두 가지 버전이 동시에 존재할 수 있습니다. 이 두 기능을 분리하려면 버전 제어를 사용할 수 있습니다. 버전 관리는 두 가지 형태로 제공됩니다
/v1.1/articles/123456789012
.
사실 버전은 리소스의 개념이 아닌 리소스의 표현만 변경합니다. 따라서 URI 구조를 변경할 필요가 없습니다. v1.1에서는 Article에 새로운 필드가 추가되었을 수 있습니다. 그러나 여전히 기사를 반환합니다. 두 번째 옵션에서는 URI가 단순하게 유지되며 소비자는 클라이언트 구현에서 해당 URI를 변경할 필요가 없습니다.
소비자가 버전 번호를 제공하지 않는 상황에 대비한 전략을 설계하는 것이 매우 중요합니다. 버전이 제공되지 않으면 오류가 발생하거나 첫 번째 버전으로 응답을 반환할 수 있습니다. 최신 안정 버전을 기본 버전으로 사용하는 경우 소비자의 클라이언트 구현에 많은 버그가 있을 수 있습니다.
는 API가 리소스를 표시하는 방식을 나타냅니다. API 엔드포인트를 호출하면 리소스가 반환됩니다. 리소스는 XML, JSON 등 모든 형식이 될 수 있습니다. 새로운 API를 설계하는 경우 JSON을 사용하는 것이 가장 좋습니다. 그러나 XML 응답을 반환하는 기존 API를 업데이트하는 경우 JSON 응답에 다른 버전을 제공할 수 있습니다.
RESTful API 설계에 대한 충분한 이론적 정보. Restify를 사용하여 블로그 API를 설계하고 구현하여 실제 사용법을 살펴보겠습니다.
RESTful API를 설계하려면 비즈니스 도메인을 분석해야 합니다. 그런 다음 리소스를 정의할 수 있습니다. Blog API에는 다음이 필요합니다.
이 API에서는 기사나 댓글을 작성하기 위해 사용자를 인증하는 방법을 다루지 않습니다. 인증 부분에 대해서는 AngularJS 및 NodeJS에 대한 토큰 기반 인증 튜토리얼을 참조할 수 있습니다.
리소스 이름이 준비되었습니다. 리소스 작업은 간단한 CRUD입니다. API 개요는 아래 표를 참조하세요.
리소스 이름 | HTTP 동사 | HTTP 방식 |
---|---|---|
기사 | 기사 작성 기사 업데이트 기사 삭제 기사 보기 |
POST /articles with Payload PUT /articles/123 with Payload DELETE /articles/123 GET /article/123 |
댓글 | 댓글 작성 댓글 업데이트 댓글 삭제 댓글 보기
|
POST /articles/123/comments with payload PUT /comments/123 with payload DELETE /comments/123 GET /comments/123 |
사용자 | 사용자 만들기 사용자 업데이트 사용자 삭제 사용자 보기 |
POST /users with Payload PUT /users/123 with Payload DELETE /users/123 GET /users/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,您将始终拥有一个灵活、可维护、易于理解的系统。
위 내용은 NodeJS와 Restify를 사용하여 RESTful API 설계의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!