Nodejs Web API 的 WCF 设计模式






4.83/5 (3投票s)
将 WCF 设计模式应用于 Nodejs Web API
引言
当你开始使用 nodejs 时,首先会遇到的问题是 Javascript 中编写代码的动态方式。 几乎不可能找到一个标准或模式来编写体面的 nodejs Web API 后端。 本文试图提出一种关于将 WCF 设计模式引入 nodejs 的理论,在阅读本文之前,您可以充分利用 https://documenter.getpostman.com/view/258093/example/2Jwb6h 。
背景
WCF 是一种很棒的设计模式; 如果我们忽略配置上的麻烦,它会保持一切就绪。 您有数据契约,它允许向消费者公开所需的数据,操作契约用于呈现服务方法,终结点是一种很好的方式来告诉如何使用服务。那么我们如何在 nodejs 中实现 WCF 设计模式。
使用代码
为了使 WCF 设计模式与 nodejs 一起工作,一切都将从文件结构开始。这是我的建议,请注意以下结构是高级别的
-App
--dataContracts
--IApp
--logs
--operationsContract
--web.config
--package.json
在下一节中,我将尝试解释每个文件夹的作用以及我们需要它的原因。
结构解释
App
应用程序的根文件夹,它包含应用程序所需的一切,例如 node_models、YAML 文件或应用程序需要的任何额外信息,都将其设置在根文件夹中,请注意 App 文件夹的名称可以是您的应用程序名称。
dataContracts
在此文件夹中,您声明需要向使用者公开的所有模型。 每个模型都将有一个文件夹;每个文件夹将有一个 index.js 文件,用于导出模型,例如,此 index.js 文件正在公开一个 mongoose 模式。
var mongoose = require('mongoose');
var schema = new mongoose.Schema({
name: String,
isActive: {
type: Boolean,
default: true
},
isDeleted: {
type: Boolean,
default: false
},
createDate: {
type: Date,
default: new Date
},
latitude: String,
longitude: String
});
module.exports = mongoose.model('categories', schema);
稍后我们将看到如何在运行应用程序之前在 mongoose 驱动程序中注册此类型,现在此文件夹的文件结构如下所示:
-IApp
--dataContracts
---applications
----index.js
---categories
----index.js
---cities
----index.js
---friendships
----index.js
---otp
----index.js
---products
----index.js
---users
----index.js
--logs
--operationsContract
--web.config
--package.json
operations contract
现在,在声明我们的数据契约之后,是时候编写操作契约了。 这部分起初可能看起来很陌生,但我的工作方式只是分离事物,因此对于每个方法,我都创建一个单独的文件,其中包含此方法的名称,然后 index 文件将导出每个方法。 例如,如果我为模型类别创建了方法 create,那么我将在 operations Contract 文件夹中创建一个名为 categories 的新文件夹。 现在在该文件夹中,我将创建一个名为 create.js 的文件,并在其中编写我的函数并将其导出为 execute,以下是代码:
var mongoose = require('mongoose');
var categoryCollection = mongoose.model('categories');
var responseDictionary = require('response-dictionary');
var verifyingCategory = require('./verifying');
exports.execute = function(httpRequest, httpResponse) {
try {
verifyingCategory.isValidForCreate(httpRequest.body, function(categoryCreationError) {
if (!categoryCreationError) {
var category = new categoryCollection(httpRequest.body);
category.save(function(savingcategoryError) {
if (!savingcategoryError) {
return responseDictionary.getAsHttpResponse(1008, category, null, httpResponse);
}
else {
return responseDictionary.getAsHttpResponse(1500, null, savingcategoryError, httpResponse);
}
});
}
else {
return httpResponse.status(categoryCreationError.httpStatus).send(categoryCreationError);
}
});
}
catch (exception) {
return responseDictionary.getAsHttpResponse(1500, null, exception, httpResponse);
}
};
您的函数可以是您想要的任何函数,但请注意,我传递了 HTTP 请求,以便我可以完全控制来自使用者的请求。 现在要公开此函数,我们使用 operations contract 中每个文件夹中的 index.js。 这是完整的 index.js 文件:
var category = function() {};
category.prototype.create = function execute(httpRequest, httpResponse) {
require('./create').execute(httpRequest, httpResponse);
};
category.prototype.update = function execute(httpRequest, httpResponse) {
require('./update').execute(httpRequest, httpResponse);
};
category.prototype.delete = function execute(httpRequest, httpResponse) {
require('./delete').execute(httpRequest, httpResponse);
};
category.prototype.get = function execute(httpRequest, httpResponse) {
require('./get').execute(httpRequest, httpResponse);
};
category.prototype.getById = function execute(httpRequest, httpResponse) {
require('./getById').execute(httpRequest, httpResponse);
};
module.exports = category;
让我们看看到目前为止的文件结构是什么样子的:
-IApp
--dataContracts
---applications
----index.js
---categories
----index.js
---cities
----index.js
---friendships
----index.js
---otp
----index.js
---products
----index.js
---users
----index.js
--logs
--operationsContract
---applications
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
---categories
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
---cities
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
---friendships
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
---otp
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
---products
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
---users
----create.js
----update.js
----delete.js
----get.js
----getById.js
----index.js
--web.config
--package.json
Web.Config
现在操作和数据契约已设置就绪,是时候将它们连接在一起了。 首先,让我们看一下 web.config 文件夹。 它有一个名为 index.js 的主文件; 让我们看一下:
module.exports = { appName: 'families', contracts: ['applications', 'cities', 'categories', 'users', 'otp', 'friendships', 'products'], responses: responses.responses, isHttpsEnabled: false, enableLogging: true, port: 8901, connectionString: "mongodb:///families", dataBaseName: "families", certificates: { key: __dirname + '/server/certificates/server.key', certificate: __dirname + '/server/certificates/server.crt' }, publicDirectories: [{ title: '/', path: __dirname + '/public' }], appSetting: { uploadingFolder: __dirname + '/public/files' } };
var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var cors = require('cors');
var https = require('https');
var fs = require("fs");
var config = require('../web.config');
var app = express();
exports.startup = function() {
mongoose.connect(config.connectionString);
app.use(bodyParser.urlencoded({
extended: true,
limit: '50mb'
}));
app.use(bodyParser.json({
limit: '50mb'
}));
app.use(cors());
for (var contract = 0; contract < config.contracts.length; contract++) {
require('../dataContracts/' + config.contracts[contract]);
}
require('../web.config/endPoints')(app);
for (var directory = 0; directory < config.publicDirectories.length; directory++) {
if (fs.existsSync(config.publicDirectories[directory].path) === false) {
fs.mkdirSync(config.publicDirectories[directory].path);
}
app.use(config.publicDirectories[directory].title, express.static(config.publicDirectories[directory].path));
}
if (config.isHttpsEnabled === true) {
https.createServer({
key: fs.readFileSync(config.certificates.key),
cert: fs.readFileSync(config.certificates.certificate)
}, app).listen(config.port);
}
else {
app.listen(config.port);
}
console.log(config.appName + ' running on: ' + config.port);
};
this.startup();
require('../web.config/endPoints')(app);
var authenticate = require('../lib/authentication');
var applications = require('../operationsContract/applications');
var categories = require('../operationsContract/categories');
var cities = require('../operationsContract/cities');
module.exports = function(app) {
app.post('/api/applications/', new authenticate().masterKey, new applications().create);
app.patch('/api/applications/:id', new authenticate().masterKey, new applications().update);
app.delete('/api/applications/:id', new authenticate().masterKey, new applications().delete);
app.get('/api/applications/', new authenticate().masterKey, new applications().get);
app.get('/api/applications/:id', new authenticate().masterKey, new applications().getById);
app.post('/api/categories/', new authenticate().masterKey, new categories().create);
app.patch('/api/categories/:id', new authenticate().masterKey, new categories().update);
app.delete('/api/categories/:id', new authenticate().masterKey, new categories().delete);
app.get('/api/categories/', new authenticate().applicationKey, new categories().get);
app.get('/api/categories/:id', new authenticate().applicationKey, new categories().getById);
app.post('/api/cities/', new authenticate().masterKey, new cities().create);
app.patch('/api/cities/:id', new authenticate().masterKey, new cities().update);
app.delete('/api/cities/:id', new authenticate().masterKey, new cities().delete);
app.get('/api/cities/', new authenticate().applicationKey, new cities().get);
app.get('/api/cities/:id', new authenticate().applicationKey, new cities().getById);
};
身份验证
任何不需要公开的函数或模块都将写入名为 lib 的文件夹中,并且由于我将完整的 HTTP 请求对象传递给它,因此您可以使用所需的任何身份验证类型,基本 HTTP、oauth 或只是标头中的密钥,例如,以下是一种验证主密钥的方法:
var responseDictionary = require('response-dictionary');
var config = require('rekuire')('web.config');
exports.execute = function(httpRequest, httpResponse, next) {
try {
if (httpRequest.header("X-example-MasterKey")) {
if (httpRequest.header("X-example-MasterKey") === config.masterKey) {
next();
}
else {
responseDictionary.getAsHttpResponse(2, null, null, httpResponse);
}
}
else {
responseDictionary.getAsHttpResponse(1, null, null, httpResponse);
}
}
catch (exception) {
responseDictionary.getAsHttpResponse(1500, null, exception, httpResponse);
}
};
最后一件事是使用以下命令运行我们的后端:
forever start IExample/