简体中文
基于 koa 风格的 uniCloud 云函数路由库,同时支持 uniCloud 客户端及 URL 化访问
源码仓库:https://gitee.com/dcloud/uni-cloud-router
从插件市场导入
管理公共模块依赖
,选择uni-cloud-router并确定使用npm安装
npm install --save uni-cloud-router
├── package.json
├── index.js // 云函数入口文件
├── config.js // 用于配置 router 应用根目录、中间件等
├── controller // 用于解析用户的输入,处理后返回相应的结果
| ├── user.js
├── service (可选) //用于编写业务逻辑层,建议使用
| ├── user.js
为了快速上手,提供了一个简单的 demo 示例,以创建是一个 hello-uni-cloud-router
云函数为例,演示如何通过 uni-cloud-router
组织代码:
1.添加入口文件
// index.js (通常无需改动)做
const Router = require("uni-cloud-router").Router; // 引入 Router
const router = new Router(require("./config.js")); // 根据 config 初始化 Router
exports.main = async (event, context) => {
return router.serve(event, context); // 由 Router 接管云函数
};
2.添加配置文件
// config.js
module.exports = {
debug: true, // 调试模式时,将返回 stack 错误堆栈
baseDir: __dirname, // 必选,应用根目录
middleware: [], // 自定义中间件
};
3.在 controller 文件夹下创建一个 hello.js
创建一个 controller
const { Controller } = require("uni-cloud-router");
module.exports = class HelloController extends Controller {
sayHello() {
return this.service.hello.sayHello();
}
};
4.在 service 文件夹下创建一个 hello.js
创建一个 service
const { Service } = require("uni-cloud-router");
module.exports = class HelloService extends Service {
sayHello() {
return {
data: "welcome to uni-cloud-router!",
};
}
};
到这里,已创建好了是一个 hello-uni-cloud-router
云函数(注意:需上传云函数后,前端才能访问)。
5.在页面里调用云函数
在页面中通过callFunction访问 hello(controller)下 sayHello:
sayHello() {
uniCloud.callFunction({
name: 'hello-uni-cloud-router',
data: {
action: 'hello/sayHello',
data: {}
}
})
.then(res => {
this.title = res.data
});
}
以上代码仅作为示例,建议点击右侧【使用 HBuilderX 导入示例项目】尝试。
负责解析用户的输入,处理后返回相应的结果。
推荐 Controller 层主要对用户的请求参数进行处理(校验、转换),然后调用对应的 service 方法处理业务,得到业务结果后封装并返回:
所有的 Controller 文件都必须放在 controller
目录下,可以支持多级目录,访问的时候可以通过目录名级联访问。
// controller/post.js
const Controller = require("uni-cloud-router").Controller;
// 必须继承 Controller 类
module.exports = class PostController extends Controller {
async create() {
const { ctx, service } = this;
// 校验参数,注意:uni-cloud-router本身不包含validate方法,此方法需要由用户自行实现建议在中间件挂载
ctx.validate({
title: { type: "string" },
content: { type: "string" },
});
// 组装参数,ctx.auth.uid是由用户自己的auth中间件挂载到ctx上的
const author = ctx.auth.uid;
const post = Object.assign(ctx.data, { author });
// 调用 Service 进行业务处理
return service.post.create(post);
}
};
定义的 Controller 类,会在每一个请求访问时实例化一个全新的对象,会有下面几个属性挂在 this
上。
this.ctx
:当前请求的上下文对象的实例,通过它我们可以拿到各种便捷属性和方法。this.service
:应用定义的 service,通过它我们可以访问到抽象出的业务层,等同于 this.ctx.service
。this.db
:等同于 uniCloud.database()
。this.curl
:等同于 uniCloud.httpclient.request
。this.throw
:抛出异常信息,等同于 this.ctx.throw
。通过在 Controller 上绑定的 Context 实例的 data 属性,获取请求发送过来的参数
class PostController extends Controller {
async listPosts() {
const data = this.ctx.data;
// {
// username: 'demo',
// password: 'demo',
// }
}
}
通过 Service 层进行业务逻辑的封装,不仅能提高代码的复用性,同时可以让业务逻辑更好测试。
Controller 中可以调用任何一个 Service 上的任何方法,同时 Service 是懒加载的,只有当访问到它的时候才会去实例化它。
class PostController extends Controller {
async create() {
const { ctx, service } = this;
const author = ctx.auth.uid;
const post = Object.assign(ctx.data, { author });
// 调用 service 进行业务处理
return service.post.create(post);
}
}
Service 的具体写法,请查看 Service 章节。
class PostController extends Controller {
async create() {
// 设置状态码为 201
this.ctx.status = 201; // 仅当使用 HTTP/HTTPS 请求时生效
}
}
class PostController extends Controller {
async create() {
this.ctx.headers = {
'location': 'http://www.baidu.com'
}
}
}
注意
// 返回javascript内容
class GetController extends Controller {
async create() {
ctx.headers = {'content-type':'application/javascript'}
ctx.body = 'console.log("abc")'
}
}
// 返回图片
class GetController extends Controller {
async create() {
ctx.isBase64Encoded = true
ctx.headers = {'content-type': 'image/png'}
ctx.body = '图片Buffer对应的base64内容'
}
}
在某些场景下,cookie依然占有重要地位,例如在云函数URL化的情况下,获取客户端的状态
在云函数中使用cookie需要依赖cookie库npm页面地址,可以通过npm install cookie
安装
'use strict';
//引入cookie
const cookie = require('cookie')
module.exports = class Instance extends Controller
{
async foo(ctx)
{
//event为客户端上传的参数
//如果客户端有cookie,则cookie回随请求携带至服务端,放置在ctx.event.headers.cookie中,假设 cookie为“[jwt=自加密base64; app=uniCloud]”
console.log('event : ', ctx.event)
//解析cookie
const cookieData = cookie.parse( ctx.event.headers.cookie||'' )
console.log(cookieData)//输出结果为:{jwt:"自加密base64", app:"uniCloud" }
//设置cookie到客户端
const cookieOptions = {
//具体参数请查阅 https://www.npmjs.com/package/cookie
maxAge: 60 * 60 * 24 * 7,//一周
path:"/"
}
const setCookieData = cookie.serialize('app', 'appName', cookieOptions)
ctx.headers['set-cookie'] = setCookieData
//...其他操作
}
};
业务逻辑封装的一个抽象层,有以下几个好处:
所有的 Service 文件都必须放在 service
目录下,可以支持多级目录,访问的时候可以通过目录名级联访问。
// service/post.js
const Service = require("uni-cloud-router").Service;
// 必须继承 Service
module.exports = class PostService extends Service {
async create(data) {
return this.db.add(data);
}
};
定义的 Service 类是懒加载的,只有当访问到它的时候才会去实例化它,会有下面几个属性挂在 this
上。
this.ctx
:当前请求的上下文对象的实例,通过它我们可以拿到各种便捷属性和方法。this.service
:应用定义的 service,通过它我们可以访问到抽象出的业务层,等同于 this.ctx.service
。this.db
:等同于 uniCloud.database()
。this.curl
:等同于 uniCloud.httpclient.request
。this.throw
:抛出异常信息,等同于 this.ctx.throw
。在路由请求前,后添加处理逻辑,实现一些特定功能,如:用户登录,权限校验等
与 koa 保持一致,参考:koa 中间件
// middleware/auth.js
const uniID = require("uni-id");
module.exports = () => {
// 返回中间件函数
return async function auth(ctx, next) {
// 校验 token
const auth = await uniID.checkToken(ctx.event.uniIdToken);
if (auth.code) {
// 校验失败,抛出错误信息
throw { code: auth.code, message: auth.message };
}
ctx.auth = auth; // 设置当前请求的 auth 对象
await next(); // 执行后续中间件
};
};
示例:
const auth = require('./middleware/auth.js') // 引入 auth 中间件
module.exports = {
debug: true, // 调试模式时,将返回 stack 错误堆栈
baseDir: __dirname, // 指定应用根目录
middleware: [
[
//数组格式,第一个元素为中间件,第二个元素为中间件生效规则配置
auth(), // 注册中间件
{ enable: true, ignore: /\/login$/ }, // 配置当前中间件生效规则,该规则表示以`/login`结尾的路由不会执行 auth 中间件校验 token
],
],
}
enable 控制中间件是否开启。
match 设置只有符合某些规则的请求才会经过这个中间件。
支持类型:
ignore 设置符合某些规则的请求不经过这个中间件。
支持类型:同 match
Context 是一个请求级别的对象,在每一次收到用户请求时,会实例化一个 Context 对象,这个对象封装了这次用户请求的信息,并提供了许多便捷的方法来获取请求参数或者设置响应信息。框架会将所有的 Service 挂载到 Context 实例上
最常见的 Context 实例获取方式是在 Middleware, Controller 以及 Service 中。
// 在 Controller 中通过 this.ctx 获取 Context 实例
module.exports = class UserController extends Controller {
async login() {
const data = this.ctx.data // 从 Context 实例上获取请求参数
}
}
// 在 Service 中通过 this.ctx 获取 Context 实例
module.exports = class PostService extends Service {
async create(data) {
const auth = this.ctx.auth // 从 Context 实例上获取 auth(需要启用 uni-id 中间件)
}
}
// 在 Middleware 中通过 ctx 参数获取 Context 实例
module.exports = (options) => {
// 返回中间件函数
return async function auth(ctx, next) {
const data = ctx.data // 从 Context 实例上获取请求参数
await next()
}
}
// 使用 uniCloud 访问
uniCloud.callFunction({
name: 'router', // 要调用的云函数名称
data: {
action: 'user/login', // 路由地址,对应 controller 下 user.js 的 login 方法
// 参数列表
data: {
// controller 通过 this.ctx.data 获取
username: 'demo',
password: 'demo',
},
},
})
// 使用 URL 化 request 访问
uni.request({
url: 'xxxxx/router/user/login', // 路由地址,对应 controller 下 user.js 的 login 方法
data: {
// controller 通过 this.ctx.data 获取
username: 'demo',
password: 'demo',
},
})
{
"code": "", // 异常 code,如:"INVOKE_FUNCTION_FAILED"
"message": "", // 异常信息
"stack": "" // 当 config.js 中配置 debug 为 true 时,返回发生异常的堆栈信息
// 其他信息
}