通過前面的使用,我們可以看出Sails中MVC的結構更傾向於MVP的概念,Presenter在Sails中被稱之爲Actions:
They often act as a middleman between your models and views.
Controller層這個結構上的變化是Sails v1.0中新提出的方案,Action可以使用classic和actions2兩種格式,另外,仍然兼容支持Sails v0.12上的Controller實現方式。相對於腫脹的Controller,將Controller拆分成多個Action可以更清晰的表達業務邏輯,其中actions2格式的Action更是一種半文檔半校驗器的形式,可以從接口描述、接口參數、參數校驗、邏輯、退出方式等方面構建整個業務邏輯。
Actions
Action2 Action
Action2格式的action與Helper基本相似,需要做調整的主要是各種與Controller層職責相關的exit方式。我們來創建一個signup action:
sails generate action user/login
我們可以編寫一個用於登錄驗證的action,從而實現登錄業務邏輯:
- Model層
Model層我們使用前面案例中的User Model:
const crypto = require('crypto');
const uuid = require('node-uuid');
module.exports = {
primaryKey: 'uuid',
attributes: {
uuid: {
type: 'string',
required: true,
},
username: {
type: 'string',
required: true,
allowNull: false,
unique: true,
},
password: {
type: 'string',
required: true,
allowNull: false,
},
},
customToJSON: function() {
return _.omit(this, ['password']);
},
beforeCreate: function (valuesToSet, proceed) {
let md5 = crypto.createHash('md5');
valuesToSet.uuid = uuid.v4();
valuesToSet.password = md5.update(valuesToSet.password).digest('hex');
return proceed();
}
};
身份驗證主要通過對username和password的查詢完成。
2. View層
首先在config/routes創建一個view路由:
'/login': {
view: 'pages/login',
locals: {
layout: 'layouts/users',
errno: 0,
},
},
該路由指定了模板爲views/pages/login,layout爲views/layouts/users(只有默認的ejs模板引擎支持此項配置),接下來我們分別創建layouts/users和pages/login:
layouts/users.ejs:
<!DOCTYPE html>
<html>
<head>
<title>Sails App</title>
<!-- Viewport mobile tag for sensible mobile support -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<!--STYLES-->
<!--STYLES END-->
</head>
<body>
<%- body %>
</body>
</html>
pages/login.ejs:
<div class="container">
<% if(errno == 1){ %>
<div class="alert alert-danger" role="alert">
Username or password is error!
</div>
<% } %>
<form method="POST" action="/user/login">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" placeholder="Enter username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Password">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
assets資源:
將bootstrap.min.css拷貝到assets/styles下,啓動時grunt任務會將其自動注入到users.ejs的樣式文件引用中。
- Controller層
api/controllers/user/login:
const crypto = require('crypto');
module.exports = {
friendlyName: 'Login',
description: 'Login user.',
inputs: {
username: {
description: 'The username',
type: 'string',
required: true,
},
password: {
description: 'The password',
type: 'string',
required: true,
}
},
exits: {
success: {
responseType: 'view',
viewTemplatePath: 'pages/welcome'
},
fail: {
responseType: 'view',
viewTemplatePath: 'pages/login'
},
err: {
description: 'Occur error',
responseType: 'notFound'
}
},
fn: async function (inputs, exits) {
let md5 = crypto.createHash('md5');
let {username, password} = inputs;
try{
let user = await User.findOne({
username, username
});
if(!user || user.password != md5.update(password).digest('hex')){
return exits.fail({
errno: 1
});
}
return exits.success({
errno: 0,
msg: {
username: username
}
});
} catch(e) {
sails.log(e.message);
throw 'err';
}
}
};
可以看到與Helper的結構是一致的,唯一需要調整的是exit的方式,而exit方式主要是由responseType決定的:
responseType is one of the following:
- “” (the standard response: Determine an appropriate response based on context: this might send plain text, download a file, transmit data as JSON, or send no response body at all.)
- “view” (render and respond with a view; exit output will be provided as view locals)
- “redirect” (redirect to the URL returned as the exit output)
爲action創建路由:
'post /user/login': 'user.login',
從代碼可以看到,登錄成功後,渲染模板pages/welcome,因此,我們創建對應的模板:
<h1>Welcome <%= msg.username %> !</h1>
接下來,我們啓動測試以下:
-
創建一個測試user:
http://127.0.0.1:1337/user/create?username=admin&password=123456&uuid=auto -
登錄錯誤:
http://127.0.0.1:1337/login
輸入錯誤的username或password:
-
登錄成功:
輸入正確的username和password:
Classic Action
Classic action與v0.12版本中傳統的Controller實現方式基本一致,將傳統寫在一個Controller中的業務邏輯拆分爲單個的一個個action,更有利於業務的實現與維護。
sails generate action user/login--no-actions2
Controller
另外,你仍然可以使用傳統的Controller方式實現所有業務邏輯:
module.exports = {
login: function (req, res) { ... },
logout: function (req, res) { ... },
signup: function (req, res) { ... },
};
machine-as-action
你也可以將Controller的編寫方式轉換到action2的編寫風格上來,請參考以下鏈接:
https://github.com/sailshq/machine-as-action