>웹 프론트엔드 >JS 튜토리얼 >Node.js 프레임워크 ThinkJS 개발 컨트롤러 설명

Node.js 프레임워크 ThinkJS 개발 컨트롤러 설명

巴扎黑
巴扎黑원래의
2017-07-17 16:00:112319검색

원본: Jingxiu.com 웹페이지 인스턴트 푸시 | 재인쇄할 경우 출처를 표시해 주세요.
링크:

이 튜토리얼 시리즈는 ThinkJS v2.x 버전(공식 웹사이트)을 예로 들어 실제 작업에 중점을 둡니다. .

이 기사에서는 컨트롤러 사용에 대해 계속 설명합니다.

생성자 메서드

객체가 인스턴스화될 때 뭔가를 하고 싶다면 생성자 메서드가 최선의 선택입니다. ES6에서 제공하는 생성자는 constructor입니다. constructor

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
方法
ECMAScript 6 入门 作者:阮一峰

init 与 constructor

thinkjs 强大的地方在于,我们不仅可以规规矩矩的 export default class 自己声明 Class ,还提供了动态创建 Class 的方法:think.controller

但是 thinkjs 动态创建的 Class 没有 constructor,而是提供了一个 init 作为构造方法的替代方法,该方法的使用方式与 constructor 一致。

上一篇文章(Node.js 国产 MVC 框架 ThinkJS 开发 controller 篇 基类与继承链部分)中也有 init 方法的使用示例,再看代码:


// 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 参数');}
  }}

当然这并不是表示不能使用 constructor 方法了,假如你是像我一样习惯使用 export default class 自己声明 Class 的筒子,还是可以用回标准的 constructor 方法的。

thinkjs 动态创建 Class 的方法参见官方文档,这里不再赘述。

魔术方法

thinkjs 实现了几个很有用的魔术方法,为开发提供了极大的便利,手动点赞~

__before 前置操作

顾名思义,前置操作会抢先在 Controller 中具体的 Action 执行之前执行,就是“在 xxx 之前执行”的意思。来看代码:


// src/home/controller/user.js'use strict';export default class extends think.controller.base {
  __before() {console.log('this is __before().');
  }
  indexAction() {console.log('this is indexAction().');return this.end();
  }}// 访问 /home/user/index 的执行结果如下:// this is __before().// this is indexAction().

那么可能有人会说:看上去 __beforeinit 是一样的用途嘛。老规矩,来看代码:


// src/home/controller/user.js'use strict';export default class extends think.controller.base {
  init(...args) {super.init(...args);console.log('this is init().');
  }
  __before() {console.log('this is __before().');
  }
  indexAction() {console.log('this is indexAction().');return this.end();
  }}// 访问 /home/user/index 的执行结果如下:// this is init().// this is __before().// this is indexAction().

看到了吗?执行还是有先后顺序的,再来个复杂一点的:


// src/home/controller/base.js'use strict';export default class extends think.controller.base {
  init(...args) {super.init(...args);console.log('this is base.init().');
  }}// src/home/controller/user.js'use strict';export default class extends think.controller.base {
  init(...args) {super.init(...args);console.log('this is user.init().');
  }
  __before() {console.log('this is user.__before().');
  }
  indexAction() {console.log('this is user.indexAction().');return this.end();
  }}// 访问 /home/user/index 的执行结果如下:// this is base.init().// this is user.init().// this is user.__before().// this is user.indexAction().

好吧,你会说“意料之中”~

__after 后置操作

明白了前置操作,后置操作也不难理解,看代码:


// src/home/controller/user.js'use strict';export default class extends think.controller.base {
  init(...args) {super.init(...args);console.log('this is init().');
  }
  __before() {console.log('this is __before().');
  }
  __after() {console.log('this is __after().');
  }
  indexAction() {console.log('this is indexAction().');return this.end();
  }}// 访问 /home/user/index 的执行结果如下:// this is init().// this is __before().// this is indexAction().

咦?貌似有地方不对。。。__after 没执行。

这当然不是 __after 写在 indexAction 上面导致的!修改代码:


// src/home/controller/user.js'use strict';export default class extends think.controller.base {
  init(...args) {super.init(...args);console.log('this is init().');
  }
  __before() {console.log('this is __before().');
  }
  __after() {console.log('this is __after().');return this.end();
  }
  indexAction() {console.log('this is indexAction().');
  }}// 访问 /home/user/index 的执行结果如下:// this is init().// this is __before().// this is indexAction().// this is __after().

这次 OK 了,和预期的结果一致。

我知道细心的你已经注意到有句代码 return this.end()indexAction 移动到 __after 里面了。

this.end() 内部执行了 Node.js HTTP response.end() 操作,表示整个响应流结束了,因此如果想要启用 __after 的话,这句代码就要放在 __after 里面运行。

__call 空操作

这个魔术方法有点特殊,它不像前两个魔术方法一样用于在某个流程节点打点运行,而是分担了 init 的部分职责:用于检测当某个 Controller 被访问的 Action 并未定义的情况下,由 __call 来接手运行。


// src/home/controller/user.js'use strict';export default class extends think.controller.base {
  init(...args) {super.init(...args);console.log('this is init().');
  }
  __call() {console.log(this.http.action + 'Action is not exists.');return this.end();
  }
  indexAction() {console.log('this is indexAction().');return this.end();
  }}// 访问 /home/user/test 的执行结果如下:// this is init().// testAction is not exists.

可以看到当访问的 testAction 不存在时,框架会运行 __call 来进行处理,我们的处理是记录错误并结束响应输出。

示例代码是将 __call 放在了二级子类中,通常是放在基类中,可以管控全部子类的非法访问处理。

提示:本方法只能用于捕捉 Action 不存在的情况,但是假如 Controller 不存在,会直接触发 404 错误(被框架接管)而无法干涉。
如要捕捉 Controller 不存在的情况,需要扩展框架的错误类,另文描述。

外部调用方式

thinkjs 官网 API 中有实例化另外一个 Controller 的接口,但是并没有说明这个具体有什么用途:


//实例化 home 模块下 user controllerlet instance = think.controller('user', http, 'home');

那么通常这个方法可以用来实例化兄弟层级 Controller ,或者获取数据、或者触发一个业务流程等,来看代码:


// src/home/controller/user.js 增加_getPoints() {
  return 8000;}// src/home/controller/index.jslet instance = think.controller('user', this.http, 'home');let points = instance._getPoints();console.log(points); // 打印:8000instance.indexAction(); // 与直接执行 /home/user/index 是一样的效果instance.testAction(); // 报错 [Error] TypeError: instance.testAction is not a function

可见是 thinkjs 提供了一个按需实例化某个 Controller 并运行其方法的途径。

乍看上去这个方式与 this.redirect 运行结果非常接近(除了不会触发 __call

🎜생성자 메서드는 클래스의 기본 메서드입니다. 이 메서드는 새 명령을 통해 개체 인스턴스가 생성될 때 자동으로 호출됩니다. 클래스에는 생성자 메서드가 있어야 합니다. 명시적으로 정의되지 않은 경우 기본적으로 빈 생성자 메서드가 추가됩니다. 🎜Method🎜ECMAScript 6 시작하기 작성자: Ruan Yifeng🎜🎜

init 및 생성자

🎜thinkjs의 장점은 기본 클래스 내보내기만 할 수 있는 것이 아니라는 것입니다. > 클래스를 직접 선언하고 동적으로 클래스를 생성하는 메소드도 제공합니다: think.controller. 🎜🎜하지만 thinkjs가 동적으로 생성한 Class에는 constructor가 없고 init를 생성자 메소드 대신 init를 제공하는데, 이는 와 같은 방식으로 사용됩니다. >생성자 코드> 일관성. 🎜🎜이전 글(Node.js 국내 MVC Framework ThinkJS 개발 컨트롤러 챕터 기본 클래스 및 상속 체인 부분)에도 init 메서드를 사용하는 예가 나와 있습니다. 코드를 다시 살펴보겠습니다. 🎜

🎜🎜

// src/home/controller/util.js'use strict';export default class extends think.controller.base {
  calcGPSDistance(lat, lng){// 计算 GPS 两点直线距离return distance;
  }
  calcBaiduDistance(lat, lng){// 计算 百度大地坐标 两点直线距离return distance;
  }
  calcSosoDistance(lat, lng){// 计算 Soso坐标 两点直线距离return distance;
  }}
🎜물론, 여러분이 나와 같고 기본 클래스 내보내기를 사용하는 데 익숙하다면 <code>constructor 메서드를 사용할 수 없다는 의미는 아닙니다. code>를 사용하여 클래스를 직접 선언하는 경우에도 표준 생성자 메서드를 사용할 수 있습니다. 🎜🎜🎜Thinkjs의 동적으로 클래스 생성 방법은 공식 문서에서 찾을 수 있으므로 여기서는 자세히 설명하지 않습니다. 🎜🎜🎜Magic method🎜🎜thinkjs는 개발에 큰 편의를 제공하는 몇 가지 매우 유용한 Magic Method를 구현했습니다. Manual like~🎜

__before pre-op

🎜이름에서 알 수 있듯이 pre-option은 선제적으로 실행됩니다. 컨트롤러의 특정 작업이 실행되기 전은 "xxx 전에 실행"을 의미합니다. 코드를 살펴보겠습니다. 🎜

🎜🎜

// src/common/controller/complete.js&#39;use strict&#39;;export default class extends think.controller.base {
  /**   * 显示中转页面   *   * 调用方式:   * let complete = think.controller(&#39;complete&#39;, this.http, &#39;common&#39;);   * return complete.display(&#39;应用新增成功!&#39;, &#39;/&#39;, 5);   *   * @param msg 提示文字,支持 HTML   * @param url 后续自动跳转的目标地址   * @param delay 停留秒数   * @returns {think.Promise}   */
  display(msg, url=&#39;&#39;, delay=3) {let tpl = &#39;common/complete/200&#39;;let opt = think.extend({}, {type: &#39;base&#39;, file_depr: &#39;_&#39;, content_type: &#39;text/html&#39;});this.fetch(tpl, {}, opt).then(content => {  content = content.replace(/COMPLETE_MESSAGE/g, msg);  if (url) {content = content.replace(/TARGET_URL/g, url);content = content.replace(/WAIT_SECONDS/g, delay);  };  this.type(opt[&#39;content_type&#39;]);  return this.end(content);}).catch(function(err){  return this.end(&#39;&#39;);});
  }}
🎜 그렇다면 일부 사람들은 다음과 같이 말할 수 있습니다. __beforeinit는 동일한 목적을 가지고 있는 것 같습니다. 평소처럼 코드를 살펴보겠습니다. 🎜

🎜🎜

<!-- view/common/complete_200.html --><!DOCTYPE html><html><head><title>正在跳转 - 荆秀网</title></head><body><p class="header"><p class="wrap"><p class="logo"><a href="/"><img src="/static/img/logo.png" alt="XxuYou" width="60"></a></p><p class="headr"> </p></p></p><p class="wrap"><p style="margin-top:20px;height:100px;background:url(/static/img/200.gif) top center no-repeat;"></p><h1>COMPLETE_MESSAGE</h1><p class="error-msg"><pre class="brush:php;toolbar:false">提示:页面将在 <span id="_count">WAIT_SECONDS</span> 秒后重定向到 <a href="TARGET_URL">TARGET_URL</a>

🎜보셨나요? 여전히 실행 순서가 있습니다. 여기에 더 복잡한 것이 있습니다. 🎜

🎜🎜

// Controller 内调用方式indexAction() {
  // 业务流程。。。
  let complete = think.controller(&#39;complete&#39;, this.http, &#39;common&#39;);
  return complete.display(&#39;操作成功!&#39;, &#39;/&#39;, 5);}
🎜좋아, "예상"이라고 말할 것입니다~🎜

__사후 작업 후

🎜이해 사전 작업, 사후 작업은 이해하기 어렵지 않습니다. 코드를 보세요: 🎜

🎜🎜rrreee🎜에? 뭔가 잘못된 것 같습니다. . . __after가 실행되지 않습니다. 🎜🎜물론 이는 indexAction 위에 __after가 쓰여서 발생한 것이 아닙니다! 수정된 코드: 🎜

🎜🎜rrreee🎜이번에는 예상한 결과와 일치하므로 괜찮습니다. 🎜🎜주의 깊게 살펴보시면 return this.end() 코드가 indexAction에서 __after로 이동되었음을 알 수 있습니다. 🎜🎜this.end()는 내부적으로 Node.js HTTP response.end() 작업을 수행하여 전체 응답 스트림이 끝났음을 나타냅니다. __after인 경우 이 코드는 __after 내에서 실행되어야 합니다. 🎜

__call no-op

🎜이 매직 메소드는 이전 두 매직 메소드처럼 특정 프로세스 노드에서 실행되는 데 사용되지 않지만 init 부분 책임: 정의되지 않은 작업이 컨트롤러에 액세스하는 경우를 감지하는 데 사용되며 <code>__call이 작업을 대신합니다. 🎜

🎜🎜rrreee🎜액세스된 testAction이 존재하지 않는 경우 프레임워크가 처리를 위해 __call을 실행하는 것을 볼 수 있습니다. 오류를 기록하고 응답 출력을 종료합니다. 🎜🎜샘플 코드는 두 번째 수준 하위 클래스, 일반적으로 기본 클래스에 __call을 배치하여 모든 하위 클래스의 불법 액세스 처리를 제어할 수 있습니다. 🎜🎜🎜팁: 이 방법은 작업이 존재하지 않는 상황을 캡처하는 데만 사용할 수 있지만 컨트롤러가 존재하지 않으면 404 오류(프레임워크에 의해 인계됨)가 직접 트리거되므로 간섭할 수 없습니다. 🎜컨트롤러가 존재하지 않는 상황을 포착하고 싶다면 프레임워크의 오류 클래스를 확장해야 합니다. 이에 대해서는 다른 글에서 설명하겠습니다. 🎜🎜🎜외부 호출 방법🎜🎜Thinkjs 공식 웹사이트 API에는 다른 컨트롤러를 인스턴스화하기 위한 인터페이스가 있지만 이것의 구체적인 사용법은 설명하지 않습니다:🎜

🎜🎜rrreee🎜그래서 일반적으로 이 방법은 다음과 같습니다. 형제 수준 컨트롤러를 인스턴스화하거나 데이터를 얻거나 비즈니스 프로세스를 트리거하는 등의 작업을 수행하려면 코드를 살펴보세요. 🎜

🎜🎜rrreee🎜thinkjs가 다음 방법을 제공하는 것을 볼 수 있습니다. 요청 시 컨트롤러를 인스턴스화하고 해당 메서드를 실행합니다. 🎜🎜얼핏 보면 이 메서드는 this.redirect의 결과와 매우 유사합니다(__call의 매직 메서드가 실행되지 않는다는 점만 제외). 이 방법을 제공하는 thinkjs를 사용합니까? 코드를 살펴보겠습니다: 🎜


// src/home/controller/util.js&#39;use strict&#39;;export default class extends think.controller.base {
  calcGPSDistance(lat, lng){// 计算 GPS 两点直线距离return distance;
  }
  calcBaiduDistance(lat, lng){// 计算 百度大地坐标 两点直线距离return distance;
  }
  calcSosoDistance(lat, lng){// 计算 Soso坐标 两点直线距离return distance;
  }}

这是一个助手 Controller,一个“隐身”的 Controller,从 url 是无法直接访问到的,因为它的所有方法名均没有 Action 后缀。

这个场景下,运行时实例化 Controller 并操作其方法的方式就派上用场了。

内置 http 对象

控制器在实例化时,会将 http 传递进去。该 http 对象是 ThinkJS 对 req 和 res 重新包装的一个对象,而非 Node.js 内置的 http 对象。
Action 里如果想获取该对象,可以通过 this.http 来获取。

thinkjs 官网

扩展应用:增加一个 n 秒后自动跳转的过渡页功能

thinkjs 框架并没有给我们准备这样一个过渡页面的功能,那么我们可以自己实现一个来练练手,上代码:


// src/common/controller/complete.js&#39;use strict&#39;;export default class extends think.controller.base {
  /**   * 显示中转页面   *   * 调用方式:   * let complete = think.controller(&#39;complete&#39;, this.http, &#39;common&#39;);   * return complete.display(&#39;应用新增成功!&#39;, &#39;/&#39;, 5);   *   * @param msg 提示文字,支持 HTML   * @param url 后续自动跳转的目标地址   * @param delay 停留秒数   * @returns {think.Promise}   */
  display(msg, url=&#39;&#39;, delay=3) {let tpl = &#39;common/complete/200&#39;;let opt = think.extend({}, {type: &#39;base&#39;, file_depr: &#39;_&#39;, content_type: &#39;text/html&#39;});this.fetch(tpl, {}, opt).then(content => {  content = content.replace(/COMPLETE_MESSAGE/g, msg);  if (url) {content = content.replace(/TARGET_URL/g, url);content = content.replace(/WAIT_SECONDS/g, delay);  };  this.type(opt[&#39;content_type&#39;]);  return this.end(content);}).catch(function(err){  return this.end(&#39;&#39;);});
  }}


<!-- view/common/complete_200.html --><!DOCTYPE html><html><head><title>正在跳转 - 荆秀网</title></head><body><p class="header"><p class="wrap"><p class="logo"><a href="/"><img src="/static/img/logo.png" alt="XxuYou" width="60"></a></p><p class="headr"> </p></p></p><p class="wrap"><p style="margin-top:20px;height:100px;background:url(/static/img/200.gif) top center no-repeat;"></p><h1>COMPLETE_MESSAGE</h1><p class="error-msg"><pre class="brush:php;toolbar:false">提示:页面将在 <span id="_count">WAIT_SECONDS</span> 秒后重定向到 <a href="TARGET_URL">TARGET_URL</a>


// Controller 内调用方式indexAction() {
  // 业务流程。。。
  let complete = think.controller(&#39;complete&#39;, this.http, &#39;common&#39;);
  return complete.display(&#39;操作成功!&#39;, &#39;/&#39;, 5);}

以上新增的 src/common/controller/complete.js 是一个非侵入式的成功业务处理页面,其内部运行与兄弟 Controller src/common/controller/error.js 类似。

以上新增的 view/common/complete_200.html 则是相关的过渡页面的模版。其中存在三个占位符(分别对应 display 方法的入參):

  • COMPLETE_MESSAGE 用于操作成功的文字提示内容

  • TARGET_URL 用于稍后会自动进入的目标 url

  • WAIT_SECONDS 用于页面过渡时间,单位是秒

实现原理其实非常简单,阅读一下两个新增的代码就能明白。

扩展应用:快速构建 REST API

其实这部分因为太简答,我本来是不想写的。不过考虑到教程的完整性,还是写一下比较好。

REST 的概念介绍这里不再赘述,有兴趣的可以自行搜索。

thinkjs 的官网说到:

自动生成 REST API,而无需写任何的代码。

此言不虚,创建 Controller 时只要增加一个参数(thinkjs controller [name] --rest),即可生成一个能够操作数据库表的 REST API。

当然操作的约定也还是有的:

  • GET /ticket #获取ticket列表

  • GET /ticket/12 #查看某个具体的ticket

  • POST /ticket #新建一个ticket

  • PUT /ticket/12 #更新ticket 12

  • DELETE /ticket/12 #删除ticekt 12

遵从了上述操作约定,的确是可以直接操作数据库表内的数据了。

只是这样的 API 只不过是一个“裸奔”状态的 API,还是不能直接投入使用。因为它不仅要求请求方熟悉所有的数据表结构,还要依赖请求方来维护多表数据之间的关联性,更不用提什么操作路径的映射、字段映射、返回数据的映射等等问题了。

就算 thinkjs 还提供了字段过滤、自定义 GET/POST/PUT/DELETE 方法来进行更多的定制,那么最终的结果很可能是在当前的 API 外面再包裹一层能够提供操作路径映射、鉴权令牌发放和识别、字段映射、关联数据维护等等。

当然作为一个开发框架,thinkjs 确实已经做的够多了,足够优秀了,因此我们还是需要像构建一个应用系统那样去完整构建一个可供实施的 REST API 的生产模型。

위 내용은 Node.js 프레임워크 ThinkJS 개발 컨트롤러 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:JavaScript 기본 설명다음 기사:JavaScript 기본 설명