控制器 控制器是一類操作的集合,用來響應(yīng)用戶同一類的請(qǐng)求。 定義控制器 創(chuàng)建文件 src/home/controller/article.js ,表示 home 模塊下有名為 article 控制器,文件內(nèi)容類似如下: "use strict";
import Base from "./base.js";
export default class extends Base {
/**
* index action
* @return {Promise} []
*/
indexAction(){
//auto render template file index_index.html
return this.display();
}
}
如果不想使用 ES6 語(yǔ)法,那么文件內(nèi)容類似如下: "use strict";
var Base = require("./base.js");
module.exports = think.controller(Base, {
/**
* index action
* @return {Promise} []
*/
indexAction: function(self){
//auto render template file index_index.html
return self.display();
}
});
注:上面的 Base 表示定義一個(gè)基類,其他的類都繼承該基類,這樣就可以在基類里做一些通用的處理。 多級(jí)控制器 對(duì)于很復(fù)雜的項(xiàng)目,一層控制器有時(shí)候不能滿足需求。這個(gè)時(shí)候可以創(chuàng)建多級(jí)控制器,如:src/home/controller/group/article.js ,這時(shí)解析到的控制器為二級(jí),具體為 group/article ,Logic 和 View 的目錄與此相同。 使用 async/await 借助 Babel 編譯,還可以在控制器里使用 ES7 里的 async/await 。 ES7 方式"use strict";
import Base from "./base.js";
export default class extends Base {
/**
* index action
* @return {Promise} []
*/
async indexAction(){
let model = this.model("user");
let data = await model.select();
return this.success(data);
}
}
動(dòng)態(tài)創(chuàng)建類的方式"use strict";
var Base = require("./base.js");
module.exports = think.controller(Base, {
/**
* index action
* @return {Promise} []
*/
indexAction: async function(){
var model = this.model("user");
var data = await model.select();
return this.success(data);
}
});
init 方法 ES6 里的 class 有 contructor 方法,但動(dòng)態(tài)創(chuàng)建的類就沒有該方法了,為了統(tǒng)一初始化執(zhí)行的方法,將該方法統(tǒng)一定義為 init 。 該方法在類實(shí)例化的時(shí)候自動(dòng)調(diào)用,無需手工調(diào)用。 ES6 方式 "use strict";
import Base from "./base.js";
export default class extends Base {
init(http){
super.init(http); //調(diào)用父類的init方法
...
}
}
動(dòng)態(tài)創(chuàng)建類方式"use strict";
var Base = require("./base.js");
module.exports = think.controller(Base, {
init: function(http){
this.super("init", http); //調(diào)用父類的init方法
...
}
});
init 方法里需要調(diào)用父類的 init 方法,并將參數(shù) http 傳遞進(jìn)去。 前置操作 __before ThinkJS 支持前置操作,方法名為 __before ,該方法會(huì)在具體的 Action 調(diào)用之前自動(dòng)調(diào)用。如果前置操作里阻止了后續(xù)代碼繼續(xù)執(zhí)行,則不會(huì)調(diào)用具體的 Action,這樣可以提前結(jié)束請(qǐng)求。
ES6 方式 "use strict";
import Base from "./base.js";
export default class extends Base {
/**
* 前置方法
* @return {Promise} []
*/
__before(){
...
}
}
Action 一個(gè) Action 代表一個(gè)要執(zhí)行的操作。如: url 為 /home/article/detail ,解析后的模塊為 /home ,控制器為 article , Action 為 detail ,那么執(zhí)行的 Action 就是文件 src/home/controller/aritcle 里的 detailAction 方法。 "use strict";
import Base from "./base.js";
export default class extends Base {
/**
* 獲取詳細(xì)信息
* @return {Promise} []
*/
detailAction(self){
...
}
}
如果解析后的 Action 值里含有 _ ,會(huì)自動(dòng)做轉(zhuǎn)化,具體的轉(zhuǎn)化策略見 路由 -> 大小寫轉(zhuǎn)化。 后置操作 __after ThinkJS 支持后置操作,方法名為 __after ,該方法會(huì)在具體的 Action 調(diào)用之后執(zhí)行。如果具體的 Action 里阻止了后續(xù)的代碼繼續(xù)執(zhí)行,則后置操作不會(huì)調(diào)用。 空操作 __call 當(dāng)解析后的 url 對(duì)應(yīng)的控制器存在,但 Action 不存在時(shí),會(huì)試圖調(diào)用控制器下的魔術(shù)方法 __call 。這里可以對(duì)不存在的方法進(jìn)行統(tǒng)一處理。 "use strict";
import Base from "./base.js";
export default class extends Base {
/**
* @return {Promise} []
*/
__call(){
...
}
}
錯(cuò)誤處理 當(dāng) url 不存在或者當(dāng)前用戶沒權(quán)限等一些異常請(qǐng)求時(shí),這時(shí)候會(huì)調(diào)用錯(cuò)誤處理。 ThinkJS 內(nèi)置了一套詳細(xì)的錯(cuò)誤處理機(jī)制,具體請(qǐng)見 擴(kuò)展功能 -> 錯(cuò)誤處理。 數(shù)據(jù)校驗(yàn) 控制器里在使用用戶提交的數(shù)據(jù)之前,需要對(duì)數(shù)據(jù)合法性進(jìn)行校驗(yàn)。為了降低控制器里的邏輯復(fù)雜度,ThinkJS 提供了一層 Logic 專門用來處理數(shù)據(jù)校驗(yàn)和權(quán)限校驗(yàn)等相關(guān)操作。 詳細(xì)信息請(qǐng)見 擴(kuò)展功能 -> Logic -> 數(shù)據(jù)校驗(yàn)。 變量賦值和模版渲染 控制器里可以通過 assign 和 display 方法進(jìn)行變量賦值和模版渲染,具體信息請(qǐng)見 這里。 模型實(shí)例化 在控制器中可以通過 this.model 方法快速獲得一個(gè)模型的實(shí)例。 export default class extends think.controller.base {
indexAction(){
let model = this.model("user"); //實(shí)例化模型 user
...
}
}
model 方法更多使用方式請(qǐng)見 API -> think.http.base。 http 對(duì)象 控制器在實(shí)例化時(shí),會(huì)將 http 傳遞進(jìn)去。該 http 對(duì)象是 ThinkJS 對(duì) req 和 res 重新包裝的一個(gè)對(duì)象,而非 Node.js 內(nèi)置的 http 對(duì)象。 Action 里如果想獲取該對(duì)象,可以通過 this.http 來獲取。 "use strict";
import Base from "./base.js";
export default class extends Base {
indexAction(){
let http = this.http;
}
}
關(guān)于 http 對(duì)象包含的屬性和方法請(qǐng)見 API -> http。 REST API 有時(shí)候,項(xiàng)目里需要提供一些 REST 接口給第三方使用,這些接口無外乎就是增刪改查等操作。 如果手工去書寫這些操作則比較麻煩,ThinkJS 提供了 REST Controller,該控制器會(huì)自動(dòng)含有通用的增刪改查等操作。如果這些操作不滿足需求,也可以進(jìn)行定制。具體請(qǐng)見 這里。 this 作用域的問題 Node.js 里經(jīng)常有很多異步操作,而異步操作常見的處理方式是使用回調(diào)函數(shù)或者 Promise。這些處理方式都會(huì)增加一層作用域,導(dǎo)致在回調(diào)函數(shù)內(nèi)無法直接使用 this ,簡(jiǎn)單的處理辦法是在頂部定義一個(gè)變量,將 this 賦值給這個(gè)變量,然后在回調(diào)函數(shù)內(nèi)使用這個(gè)變量。如: module.exports = think.controller({
indexAction: function(){
var self = this; //這里將 this 賦值給變量 self,然后在后面的回調(diào)函數(shù)里都使用 self
this.model("user").find().then(function(data){
return self.model("article").where({user_id: data.id}).select();
}).then(function(data){
self.success(data);
})
}
})
如果每個(gè) Action 里都要使用者手工寫一個(gè) var self = this ,勢(shì)必比較麻煩。為了解決這個(gè)問題,ThinkJS 在 Action 里直接提供了一個(gè)參數(shù),這個(gè)參數(shù)等同于 var self = this
,具體如下: module.exports = think.controller({
//參數(shù) self 等同于 var self = this
indexAction: function(self){
this.model("user").find().then(function(data){
return self.model("article").where({user_id: data.id}).select();
}).then(function(data){
self.success(data);
})
}
})
當(dāng)然更好的解決辦法是推薦使用 ES6 里的 Generator Function 和 Arrow Function,這樣就可以徹底解決 this 作用域的問題。 使用 Generator Functionexport default class extends think.controller.base {
* indexAction(){
let data = yield this.model("user").find();
let result = yield this.model("article").where({user_id: data.id}).select();
this.success(result);
}
}
使用 Arrow Functionmodule.exports = think.controller({
indexAction: function(){
this.model("user").find().then(data => {
return this.model("article").where({user_id: data.id}).select();
}).then(data => {
this.success(data);
})
}
})
JSON 輸出 項(xiàng)目中經(jīng)常要提供一些接口,這些接口一般都是直接輸出 JSON 格式的數(shù)據(jù),并且會(huì)有標(biāo)識(shí)表明當(dāng)前接口是否正常。如果發(fā)生異常,需要將對(duì)應(yīng)的錯(cuò)誤信息隨著接口一起輸出??刂破骼锾峁┝?this.success 和 this.fail 方法來輸出這樣的接口數(shù)據(jù)。 輸出正常的 JSON 可以通過 this.success 方法輸出正常的接口數(shù)據(jù),如: export default class extends think.controller.base {
indexAction(){
let data = {name: "thinkjs"};
this.success(data);
}
}
輸出結(jié)果為 {errno: 0, errmsg: "", data: {"name": "thinkjs"}} ,客戶端可以通過 errno 是否為 0 來判斷當(dāng)前接口是否有異常。 輸出含有錯(cuò)誤信息的 JSON 可以通過 this.fail 方法輸出含有錯(cuò)誤信息的接口數(shù)據(jù),如: export default class extends think.controller.base {
indexAction(){
this.fail(1000, "connect error"); //指定錯(cuò)誤號(hào)和錯(cuò)誤信息
}
}
輸出結(jié)果為 {errno: 1000, errmsg: "connect error"} ,客戶端判斷 errno 大于 0,就知道當(dāng)前接口有異常,并且通過 errmsg 拿到具體的錯(cuò)誤信息。 配置錯(cuò)誤號(hào)和錯(cuò)誤信息 如果每個(gè)地方輸出錯(cuò)誤的時(shí)候都要指定錯(cuò)誤號(hào)和錯(cuò)誤信息勢(shì)必比較麻煩,比較好的方式是把錯(cuò)誤號(hào)和錯(cuò)誤信息在一個(gè)地方配置,然后輸出的時(shí)候只要指定錯(cuò)誤號(hào),錯(cuò)誤信息根據(jù)錯(cuò)誤號(hào)自動(dòng)讀取。 錯(cuò)誤信息支持國(guó)際化,所以配置放在 src/common/config/locale/[lang].js 文件中。如: export default {
10001: "get data error"
}
通過上面的配置后,執(zhí)行 this.fail(10001) 時(shí)會(huì)自動(dòng)讀取到對(duì)應(yīng)的錯(cuò)誤信息。 友好的錯(cuò)誤號(hào) 在程序里執(zhí)行 this.fail(10001) 雖然能輸出正確的錯(cuò)誤號(hào)和錯(cuò)誤信息,但人不能直觀的看出來錯(cuò)誤號(hào)對(duì)應(yīng)的錯(cuò)誤信息是什么。 這時(shí)可以將 key 配置為大寫字符串,值為錯(cuò)誤號(hào)和錯(cuò)誤信息。如: export default {
GET_DATA_ERROR: [1234, "get data error"] //key 必須為大寫字符或者下劃線才有效
}
執(zhí)行 this.fail('GET_DATA_ERROR') 時(shí)也會(huì)自動(dòng)取到對(duì)應(yīng)的錯(cuò)誤號(hào)和錯(cuò)誤信息。 格式配置 默認(rèn)輸出的錯(cuò)誤號(hào)的 key 為 errno ,錯(cuò)誤信息的 key 為 errmsg 。如果不滿足需求的話,可以修改配置文件 src/common/config/error.js 。 export default {
key: "errno", //error number
msg: "errmsg", //error message
}
輸出不包含錯(cuò)誤信息的 JSON 如果輸出的 JSON 數(shù)據(jù)里不想包含 errno 和 errmsg 的話,可以通過 this.json 方法輸出 JSON。如: export default class extends think.controller.base {
indexAction(){
this.json({name: "thinkjs"});
}
}
常用功能 獲取 GET 參數(shù) 可以通過 get 方法獲取 GET 參數(shù),如: export default class extends think.controller.base {
indexAction(){
let name = this.get("name");
let allParams = this.get(); //獲取所有 GET 參數(shù)
}
}
如果參數(shù)不存在,那么值為空字符串。 獲取 POST 參數(shù) 可以通過 post 方法獲取 POST 參數(shù),如: export default class extends think.controller.base {
indexAction(){
let name = this.post("name");
let allParams = this.post(); //獲取所有 POST 參數(shù)
}
}
如果參數(shù)不存在,那么值為空字符串。 獲取上傳的文件 可以通過 file 方法獲取上傳的文件,如: export default class extends think.controller.base {
indexAction(){
let file = this.file("image");
let allFiles = this.file(); //獲取所有上傳的文件
}
}
返回值是個(gè)對(duì)象,包含下面的屬性: {
fieldName: "file", //表單字段名稱
originalFilename: filename, //原始的文件名
path: filepath, //文件保存的臨時(shí)路徑,使用時(shí)需要將其移動(dòng)到項(xiàng)目里的目錄,否則請(qǐng)求結(jié)束時(shí)會(huì)被刪除
size: 1000 //文件大小
}
如果文件不存在,那么值為一個(gè)空對(duì)象 {} 。 JSONP 格式數(shù)據(jù)輸出 可以通過 this.jsonp 方法輸出 JSONP 格式的數(shù)據(jù),callback 的請(qǐng)求參數(shù)名默認(rèn)為 callback 。如果需要修改請(qǐng)求參數(shù)名,可以通過修改配置 callback_name 來完成。 更多方法 isGet() 當(dāng)前是否是 GET 請(qǐng)求 isPost() 當(dāng)前是否是 POST 請(qǐng)求 isAjax() 是否是 AJAX 請(qǐng)求 ip() 獲取請(qǐng)求用戶的 ip redirect(url) 跳轉(zhuǎn)到一個(gè) url write(data) 輸出數(shù)據(jù),會(huì)自動(dòng)調(diào)用 JSON.stringify end(data) 結(jié)束當(dāng)前的 http 請(qǐng)求 json(data) 輸出 JSON 數(shù)據(jù),自動(dòng)發(fā)送 JSON 相關(guān)的 Content-Type jsonp(data) 輸出 JSONP 數(shù)據(jù),請(qǐng)求參數(shù)名默認(rèn)為 callback success(data) 輸出一個(gè)正常的 JSON 數(shù)據(jù),數(shù)據(jù)格式為 {errno: 0, errmsg: "", data: data} fail(errno, errmsg, data) 輸出一個(gè)錯(cuò)誤的 JSON 數(shù)據(jù),數(shù)據(jù)格式為 {errno: errno_value, errmsg: string, data: data} download(file) 下載文件 assign(name, value) 設(shè)置模版變量 display() 輸出一個(gè)模版 fetch() 渲染模版并獲取內(nèi)容 cookie(name, value) 獲取或者設(shè)置 cookie session(name, value) 獲取或者設(shè)置 session header(name, value) 獲取或者設(shè)置 header action(name, data) 調(diào)用其他 Controller 的方法,可以跨模塊 model(name, options) 獲取模型實(shí)例 完整方法列表請(qǐng)見 API -> Controller。
|