Node.js 国产 MVC 框架 ThinkJS 开发 controller 篇
2017-07-15 00:00
447 查看
原创:荆秀网 网页即时推送 https://xxuyou.com | 转载请注明出处
链接:https://blog.xxuyou.com/nodejs-thinkjs-study-controller/
本篇目录
<!-- toc -->
本系列教程以 ThinkJS v2.x 版本(官网)为例进行介绍,教程以实际操作为主。
Controller 作为接收用户输入、业务流程处理、响应处理展示的“处理器”,其构成和实现也有非常多的方式方法,以及技巧。
thinkjs 要求凡是公开出来可以被访问的方法名都要增加
暂且忽略
OK,按照自己的需要去组织 Controller 内的 Action 即可,来看代码:
注意第四、第五个方法使用了多个单词的驼峰命名(有强迫症的筒子要开心了~),这种情况下访问 url 就会有些不同了。
另外可以看到,第六和第七个方法不带
so,就是这么简单,接下来就是考虑怎么去布局 Controller 的方法了。
注:thinkjs 路由默认是强制小写英文字母的,这一点在开发中要注意。
如果继续使用
这样我们可以迅速建立起两层 Class 的继承链。这个特性你会想到怎么用?没错,用户 Session 的检测和处理,来看代码:
这样可以把强关联的所有公共业务方法统统放置在这里。之所以说强关联,表示符合下列情况的方法可以考虑放在基类中:
业务相关:与用户业务流程无关的方法不要放在这里(例如日期格式化这种的方法应当放置在全局函数中)
方法调用方:超过一个的(例如 Session 检测方法在后台模块的几乎所有 Controller 子类都会用到)
方法调用次数:超过一次的
关于业务相关性的理解人人不同,这里仅做示例而不是定论,开发人员大可按照自己的理解去划分业务边界,本文主要专注于框架的使用。
前面提到的放置公共业务方法是基类的一种玩法,可是基类还有一种玩法:使用逻辑方法来处理中断或者跳转,来看代码:
假如 url 中缺少 auth 参数,那么 Class 会初始化失败,提示错误:
如果是页面访问,也可以重定向到其他 Controller 处理页面。
可能有的筒子不喜欢一个一个的写参数名(或者需要对参数排序计算签名),那么 get 方法如果没有入参,则获取到所有 get 参数:
获取所有 post 参数:
thinkjs 提供了一个
这是一个简洁明了的 thinkjs 文件上传demo,可以看到其中的工作方法。
不过官网没有说明的是同时上传多个文件的返回数据的结构,试一下就知道!来看代码:
实践出zhen知~ 如果是多个文件上传,服务端
其中 path 属性是到临时文件的位置。如果整个上传业务逻辑正确,应当主动将文件从临时位置中移走,例如移动到
ThinkJS 默认支持的模版引擎有:
这个过程简化到两个方法即可完成这一连串工作任务委托:
看一下
实质上
箭头所指就是 ResponseHeader 内容(还包括一个 X-Powered-By 字段,嘿嘿~)。
注:更详细的模版引擎工作方式会另文描述。
作为 REST API 接口给请求方返回数据
给 AJAX 请求返回数据
thinkjs 提供了
可以看到入參数据被装载在属性 data 中(这个属性名恒定为 data 不可更改)。
这两个方法返回标准的 JSON 响应正文格式,有两个 JSON 属性用于自我描述结果:
errno 错误编号,等于 0 表示没有错误,可以读取 data 属性;大于 0 表示出现错误
errmsg 错误描述,errno 大于 0 的时候有值,可以用来提示用户
假如觉得
假如不想使用 thinkjs 标准的 JSON 响应正文格式,需要自行定义正文格式,thinkjs 也提供了
thinkjs 提供的方法是
下面我们简单实现一个 JSONP 业务看看:
配置 JSONP 的 callback 参数名,此参数为全局有效,定义一次即可
客户端 js 发起 JSONP 请求(注意
服务端 获取用户详情数据
服务端输出的 JSONP 正文
未完待续~
上一篇:Node.js 国产 MVC 框架 ThinkJS 开发 config 篇
下一篇:Node.js 国产 MVC 框架 ThinkJS 开发 controller 篇(续)
原创:荆秀网 网页即时推送 https://xxuyou.com | 转载请注明出处
链接:https://blog.xxuyou.com/nodejs-thinkjs-study-controller/
链接:https://blog.xxuyou.com/nodejs-thinkjs-study-controller/
本篇目录
<!-- toc -->
本系列教程以 ThinkJS v2.x 版本(官网)为例进行介绍,教程以实际操作为主。
Controller 基本应用
Controller 作为 MVC 框架的主力担当,是开发人员接触最多的部分。在开发过程中通常按照需求、业务流程、任务派发等,都是以一个或者多个 Controller 为边界进行划分。Controller 作为接收用户输入、业务流程处理、响应处理展示的“处理器”,其构成和实现也有非常多的方式方法,以及技巧。
Action 定义
从外部用户(使用者)感知角度来看,最先体现 Controller 的地方就是输入一个 url 所能到达的地方,url 代表着用户输入、流程跳转等动作。例如:domain.com/home/user/login domain.com/?m=home&c=user&a=login domain.com/home/order/detail/id/856 domain.com/?m=home&c=order&a=detail&id=856
thinkjs 要求凡是公开出来可以被访问的方法名都要增加
Action的后缀,例如
indexAction,来看代码:
// src/home/controller/user.js export default class extends Base { indexAction(){ // } }
暂且忽略
indexAction方法内部的实现,想要访问到这个方法的 url 组成规则是 /module/controller/action (注:这是默认路由,可以通过更改路由规则改变 url 的组成方式,后文详述),也就是
/home/user/index。
OK,按照自己的需要去组织 Controller 内的 Action 即可,来看代码:
// src/home/controller/user.js export default class extends Base { indexAction(){ // 访问 url:domain.com/home/user/index } listAction(){ // 访问 url:domain.com/home/user/list } detailAction(){ // 访问 url:domain.com/home/user/detail } orderListAction(){ // 访问 url:domain.com/home/user/order_list } orderDetailAction(){ // 访问 url:domain.com/home/user/order_detail } _getPoints(){ // private function } _getBalance(){ // private function } // etc... }
注意第四、第五个方法使用了多个单词的驼峰命名(有强迫症的筒子要开心了~),这种情况下访问 url 就会有些不同了。
另外可以看到,第六和第七个方法不带
Action后缀,这会被识别为私有方法(ES6 仍然不提供 private 关键字来标记私有方法,因此方法名前使用一个下划线前缀来标识)。
so,就是这么简单,接下来就是考虑怎么去布局 Controller 的方法了。
注:thinkjs 路由默认是强制小写英文字母的,这一点在开发中要注意。
基类与继承链
如果使用thinkjs module [moduleName]命令来创建一个模块,那么该模块的
Controller都会存在一个基类 Base(
base.js)。
# 默认生成的代码清单 src/home/ ├── config │ └── config.js ├── controller │ ├── base.js │ └── index.js ├── logic │ └── index.js └── model └── index.js
如果继续使用
thinkjs controller [moduleName/][controllerName]命令来创建每个
Controller,那么每个
Controller都会继承此基类。
import Base from './base.js';
这样我们可以迅速建立起两层 Class 的继承链。这个特性你会想到怎么用?没错,用户 Session 的检测和处理,来看代码:
// src/home/controller/base.js 'use strict'; export default class extends think.controller.base { init(...args) { super.init(...args); } /** * 检测session数据 * 如果有问题就返回false * 如果OK就续命 * @returns {boolean} * @private */ async checkSession() { let userSess = await this.session('be_user'); if (think.isEmpty(userSess)) return false; let userToken = userSess['token']; if (think.isEmpty(userToken)) return false; if (/^[a-z0-9]{128}$/.test(userToken) == false) return false; let userExpire = userSess['expire']; let now = +(new Date); if (now >= userExpire) return false; userSess['expire'] = now + this.config('backend.user')['session_life']; await this.session('be_user', userSess); return true; } }
这样可以把强关联的所有公共业务方法统统放置在这里。之所以说强关联,表示符合下列情况的方法可以考虑放在基类中:
业务相关:与用户业务流程无关的方法不要放在这里(例如日期格式化这种的方法应当放置在全局函数中)
方法调用方:超过一个的(例如 Session 检测方法在后台模块的几乎所有 Controller 子类都会用到)
方法调用次数:超过一次的
关于业务相关性的理解人人不同,这里仅做示例而不是定论,开发人员大可按照自己的理解去划分业务边界,本文主要专注于框架的使用。
前面提到的放置公共业务方法是基类的一种玩法,可是基类还有一种玩法:使用逻辑方法来处理中断或者跳转,来看代码:
// src/home/controller/base.js 'use strict'; export default class extends think.controller.base { init(...args) { super.init(...args); // 要求所有 url 必须携带 auth 参数 let auth = this.get('auth'); if (think.isEmpty(auth)) { return this.error(500, '所有 url 必须携带 auth 参数'); } } }
假如 url 中缺少 auth 参数,那么 Class 会初始化失败,提示错误:
{ "errno": 500, "errmsg": "所有 url 必须携带 auth 参数" }
如果是页面访问,也可以重定向到其他 Controller 处理页面。
表单提交与处理
除了通常的页面切换,Controller 还有一个重要的工作就是处理用户数据,其中以表单提交(以及 AJAX 提交)为重。GET 提交/访问
thinkjs 提供了this.get([paramName])方法来获取 GET 参数。
let auth = this.get('auth'); console.log(auth); // 打印:xyz
可能有的筒子不喜欢一个一个的写参数名(或者需要对参数排序计算签名),那么 get 方法如果没有入参,则获取到所有 get 参数:
let params = this.get(); console.log(params); // 打印:{ auth: 'xyz' }
POST 提交
thinkjs 提供了this.post()方法来获取 POST 参数。
let auth = this.post('auth'); console.log(auth); // 打印:xyz
获取所有 post 参数:
let params = this.post(); console.log(params); // 打印:{ auth: 'xyz' }
上传文件
如果需要接收用户提交的二进制流,需要给form元素增加属性
enctype来指定上传的内容类型 :
<form name="formName" method="POST" enctype="multipart/form-data"> <input type="file" name="myFile" /> </form>
thinkjs 提供了一个
this.file([fileName])方法,这样可以很方便的处理上传文件了(开发人员并不需要自己拼接二进制块,上传文件已经被框架接收,并保存在系统临时目录中,方法返回的只是一个包含相关信息的 Object)。
这是一个简洁明了的 thinkjs 文件上传demo,可以看到其中的工作方法。
不过官网没有说明的是同时上传多个文件的返回数据的结构,试一下就知道!来看代码:
<form name="formName" method="POST" enctype="multipart/form-data"> <input type="file" name="myFile1" /> <input type="file" name="myFile2" /> <input type="submit" name="Submit" /> </form>
let files = think.extend({}, this.file()); console.log(files);
{ "myFile1": { "fieldName": 'myFile1', "originalFilename": '查询身份证绑定的公众号.jpg', "path": '/data/www/thinkjs_module/runtime/upload/twLYslNHfLzWxFaGR2Rqg_qb.jpg', "headers": { "content-disposition": 'form-data;name="myFile1";filename="查询身份证绑定的公众号.jpg"', "content-type": 'image/jpeg' }, "size": 86411 }, "myFile2": { "fieldName": 'myFile2', "originalFilename": '查询微信号绑定的公众号.jpg', "path": '/data/www/thinkjs_module/runtime/upload/EP6KoSAMxlL9vU4uTFviNs7d.jpg', "headers": { "content-disposition": 'form-data;name="myFile2";filename="查询微信号绑定的公众号.jpg"', "content-type": 'image/jpeg' }, "size": 95865 } }
实践出zhen知~ 如果是多个文件上传,服务端
this.file()返回的数据是以
input.name为属性的映射关系,处理起来非常方便。
其中 path 属性是到临时文件的位置。如果整个上传业务逻辑正确,应当主动将文件从临时位置中移走,例如移动到
www/static/upload/中;如果服务端代码没有将文件移动到其他位置,那么最终框架会自动删除临时文件。
输出到响应
处理完了用户数据,最终需要向客户端浏览器返回内容。返回内容的处理不属于 Controller 的工作范畴(当然你可以用 Controller 也是可以做到的)。这个过程就是 Controller 挑选模版,给定数据(变量),然后统统交给模版引擎来处理。ThinkJS 默认支持的模版引擎有:
ejs,
jade,
swig和
nunjucks,默认模版引擎为
ejs,可以根据需要修改为其他的模版引擎。(来自官网文档)
这个过程简化到两个方法即可完成这一连串工作任务委托:
this.assign(dataName, data)将变量指派给模版引擎,并命名方便模版引擎调用
this.display([viewFileName])显示模版引擎渲染后的结果
看一下
this.display()的工作细节:
// src/home/controller/index.js indexAction() { return this.display(); // 系统会去找 view/home/index_index.html 来渲染并输出到响应 } listAction(){ return this.display(); // 系统会去找 view/home/index_list.html 来渲染并输出到响应 }
实质上
this.display()所做的远不止我们看到的这么少,除了输出响应正文(ResponseBody,一堆的 HTML 代码让浏览器去解析),还负责输出合法正确的响应头(ResponseHeader,返回网络响应状态、响应内容类型、响应正文编码等)。看图:
箭头所指就是 ResponseHeader 内容(还包括一个 X-Powered-By 字段,嘿嘿~)。
注:更详细的模版引擎工作方式会另文描述。
输出 JSON 到响应
Controller 默认输出的响应content-type是
text/html主要用于页面显示。但是以下两种情况下需要 Controller 输出 JSON 格式的响应:
作为 REST API 接口给请求方返回数据
给 AJAX 请求返回数据
thinkjs 提供了
this.success和
this.fail来负责输出标准的 JSON 响应,如前所述,这两个方法同样能够返回正确的响应头(
content-type是
application/json)。
this.success方法接受一个入参,可以是
Array也可以是
Object,可根据业务需要自行组织结构和内容,调用之后返回的响应正文是一个统一格式的 JSON,如:
{ "errno": 0, "errmsg": "", "data": { "id": 234, "user": "test" } }
可以看到入參数据被装载在属性 data 中(这个属性名恒定为 data 不可更改)。
this.fail方法接受两个入參:错误编号和错误文本,两个参数均可根据业务需要自行组织,调用之后返回的响应正文是一个统一格式的 JSON,如:
{ "errno": 90001, "errmsg": "缺少必要参数" }
这两个方法返回标准的 JSON 响应正文格式,有两个 JSON 属性用于自我描述结果:
errno 错误编号,等于 0 表示没有错误,可以读取 data 属性;大于 0 表示出现错误
errmsg 错误描述,errno 大于 0 的时候有值,可以用来提示用户
假如觉得
errno和
errmsg这两个字段名不合适需要修改为其他名字的(比如我习惯使用
err和
msg),可以修改
src/common/config/error.js文件达到目的。
假如不想使用 thinkjs 标准的 JSON 响应正文格式,需要自行定义正文格式,thinkjs 也提供了
this.json方法,传入一个
Array或者
Object参数,方法会自动对参数执行
JSON.stringify方法转化为格式良好的 JSON 响应正文。
输出 JSONP
Controller 既然可以输出 JSON,那么输出 JSONP 也是没跑了~thinkjs 提供的方法是
this.jsonp。callback 的请求参数名默认为
callback。如果需要修改请求参数名,可以通过修改配置
callback_name来完成。
下面我们简单实现一个 JSONP 业务看看:
配置 JSONP 的 callback 参数名,此参数为全局有效,定义一次即可
// src/common/config/config.js export default { // jsonp 请求的 callback 参数名,此参数名要告知前端开发人员 callback_name: 'callbacks' }
客户端 js 发起 JSONP 请求(注意
jsonp和
jsonpCallback这两个参数的值)
$.ajax({ url : '/home/user/ajax_get_user_info', dataType : 'jsonp', jsonp : 'callbacks', jsonpCallback: 'myfunc', success : function(res, textStatus) { console.log(res); } });
服务端 获取用户详情数据
async ajaxGetUserInfoAction(){ if (!this.isAjax()) return this.fail(90001, 'Request must be AJAX'); let sess = await this.session('front'); let userModel = this.model('user'); let userInfo = await userModel.field('name,nickname,email').find(sess['id']); if (think.isEmpty(userInfo)) return this.fail(10001, 'user is not exists!'); return this.jsonp(userInfo); }
服务端输出的 JSONP 正文
myfunc({"name": "xxuyou", "nickname": "荆秀网", "email": "cap@xxuyou.com"})
未完待续~
上一篇:Node.js 国产 MVC 框架 ThinkJS 开发 config 篇
下一篇:Node.js 国产 MVC 框架 ThinkJS 开发 controller 篇(续)
原创:荆秀网 网页即时推送 https://xxuyou.com | 转载请注明出处
链接:https://blog.xxuyou.com/nodejs-thinkjs-study-controller/
相关文章推荐
- Node.js 国产 MVC 框架 ThinkJS 开发 controller 篇(续)
- Node.js 国产 MVC 框架 ThinkJS 开发 入门
- Node.js 国产 MVC 框架 ThinkJS 开发 config 篇
- 全端开发必备!10个最好的 Node.js MVC 框架
- 全端开发必备!10个最好的 Node.js MVC 框架
- [导入]ASP.NET MVC框架开发系列课程(5):控制器(Controller)以及Action.zip(25.35 MB)
- 基于 Node.js 平台,快速、开放、极简的 web 开发框架。
- Express 基于 Node.js 平台,快速、开放、极简的 web 开发框架。
- Node.js开发框架Express4.x
- 框架开发之——AngularJS+MVC+Routing开发步骤总结——5.14
- 分享一个基于 Node.js 的 Web 开发框架 - Nokitjs
- 教你如何选择Node.js Web开发框架?
- (pomelo系列入门教材)深入浅出node.js游戏服务器开发1——基础架构与框架介绍
- 我用的一些Node.js开发工具、开发包、框架等总结,node.js开发包
- Node.js开发框架Express4.x
- 如何选择Node.js Web开发框架?
- 【后台】开发框架之Node.js
- 推荐 21 款优秀的高性能 Node.js 开发框架
- Node.js 入门手册:那些最流行的 Web 开发框架