English
UniCloud cloud function routing library based on koa style, supports both uniCloud client and URL access
Source code repository: https://gitee.com/dcloud/uni-cloud-router
Imported from Plugin Market
Manage Common Module Dependencies
in the cloud function directory where you want to use uni-cloud-router (for example: uniCloud/cloudfunctions/router), select uni-cloud-router and confirmInstall using npm
npm install --save uni-cloud-router
├── package.json
├── index.js // 云函数入口文件
├── config.js // 用于配置 router 应用根目录、中间件等
├── controller // 用于解析用户的输入,处理后返回相应的结果
| ├── user.js
├── service (可选) //用于编写业务逻辑层,建议使用
| ├── user.js
In order to get started quickly, a simple demo example is provided, taking creating a hello-uni-cloud-router
cloud function as an example, to demonstrate how to organize code through uni-cloud-router
:
1. Add entry file
// index.js (usually without changes) do
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. Add configuration file
// config.js
module.exports = {
debug: true, // 调试模式时,将返回 stack 错误堆栈
baseDir: __dirname, // 必选,应用根目录
middleware: [], // 自定义中间件
};
3. Create a hello.js in the controller folder
create a controller
const { Controller } = require("uni-cloud-router");
module.exports = class HelloController extends Controller {
sayHello() {
return this.service.hello.sayHello();
}
};
4. Create a hello.js in the service folder
create a service
const { Service } = require("uni-cloud-router");
module.exports = class HelloService extends Service {
sayHello() {
return {
data: "welcome to uni-cloud-router!",
};
}
};
At this point, a hello-uni-cloud-router
cloud function has been created (note: the front-end can only be accessed after uploading the cloud function).
5. Call the cloud function in the page
In the page, access sayHello under hello (controller) through callFunction:
sayHello() {
uniCloud.callFunction({
name: 'hello-uni-cloud-router',
data: {
action: 'hello/sayHello',
data: {}
}
})
.then(res => {
this.title = res.data
});
}
The above code is only an example, it is recommended to click [Import sample project using HBuilderX] on the right to try.
It is responsible for parsing the user's input and returning the corresponding result after processing.
It is recommended that the Controller layer mainly process (check and convert) the user's request parameters, and then call the corresponding service method to process the business, encapsulate and return the business result after obtaining the business result:
All Controller files must be placed in the controller
directory, which can support multi-level directories, and can be accessed by cascading directory names when accessing.
// controller/post.js
const Controller = require("uni-cloud-router").Controller;
// Must inherit from Controller class
module.exports = class PostController extends Controller {
async create() {
const { ctx, service } = this;
// Verify parameters, note: uni-cloud-router itself does not contain a validate method, this method needs to be implemented by the user. It is recommended to mount it in the middleware
ctx.validate({
title: { type: "string" },
content: { type: "string" },
});
// Assembly parameters, ctx.auth.uid is mounted on ctx by the user's own auth middleware
const author = ctx.auth.uid;
const post = Object.assign(ctx.data, { author });
// Call Service for business processing
return service.post.create(post);
}
};
The defined Controller class will instantiate a brand new object every time an access request is made, and the following properties will be attached to this
.
this.ctx
: The instance of the context object of the current request, through which we can get various convenient properties and methods.this.service
: The service defined by the application, through which we can access the abstracted business layer, which is equivalent to this.ctx.service
.this.db
: equivalent to uniCloud.database()
.this.curl
: equivalent to uniCloud.httpclient.request
.this.throw
: throw exception information, equivalent to this.ctx.throw
.Obtain the parameters sent by the request through the data attribute of the Context instance bound on the Controller
class PostController extends Controller {
async listPosts() {
const data = this.ctx.data;
// {
// username: 'demo',
// password: 'demo',
// }
}
}
The encapsulation of business logic through the Service layer can not only improve the reusability of the code, but also allow the business logic to be better tested.
The Controller can call any method on any Service, and the Service is lazy loaded, and it will be instantiated only when it is accessed.
class PostController extends Controller {
async create() {
const { ctx, service } = this;
const author = ctx.auth.uid;
const post = Object.assign(ctx.data, { author });
// Call service for business processing
return service.post.create(post);
}
}
For the specific writing of Service, please refer to the Service chapter.
class PostController extends Controller {
async create() {
// set the status code to 201
this.ctx.status = 201; // 仅当使用 HTTP/HTTPS 请求时生效
}
}
class PostController extends Controller {
async create() {
this.ctx.headers = {
'location': 'http://www.baidu.com'
}
}
}
Notice
// return javascript content
class GetController extends Controller {
async create() {
ctx.headers = {'content-type':'application/javascript'}
ctx.body = 'console.log("abc")'
}
}
// return image
class GetController extends Controller {
async create() {
ctx.isBase64Encoded = true
ctx.headers = {'content-type': 'image/png'}
ctx.body = '图片Buffer对应的base64内容'
}
}
In some scenarios, cookies still play an important role, for example, in the case of URLization of cloud functions, to obtain the status of the client
Using cookies in cloud functions needs to rely on the cookie library npm page address, which can be installed through npm install cookie
'use strict';
//Introduce cookies
const cookie = require('cookie')
module.exports = class Instance extends Controller
{
async foo(ctx)
{
//event is the parameter uploaded by the client
//If the client has a cookie, the cookie will be carried to the server with the request and placed in ctx.event.headers.cookie, assuming the cookie is "[jwt=self-encrypting base64; app=uniCloud]"
console.log('event : ', ctx.event)
//parse the cookie
const cookieData = cookie.parse( ctx.event.headers.cookie||'' )
console.log(cookieData)//输出结果为:{jwt:"自加密base64", app:"uniCloud" }
//Set the cookie to the client
const cookieOptions = {
//Please refer to https://www.npmjs.com/package/cookie for specific parameters
maxAge: 60 * 60 * 24 * 7,//一周
path:"/"
}
const setCookieData = cookie.serialize('app', 'appName', cookieOptions)
ctx.headers['set-cookie'] = setCookieData
//...other operations
}
};
An abstraction layer for business logic encapsulation, which has the following benefits:
All Service files must be placed in the service
directory, which can support multi-level directories, and can be accessed by cascading directory names when accessing.
// service/post.js
const Service = require("uni-cloud-router").Service;
// must inherit from Service
module.exports = class PostService extends Service {
async create(data) {
return this.db.add(data);
}
};
The defined Service class is lazy loaded, it will be instantiated only when it is accessed, and there will be the following properties hanging on this
.
this.ctx
: The instance of the context object of the current request, through which we can get various convenient properties and methods.this.service
: The service defined by the application, through which we can access the abstracted business layer, which is equivalent to this.ctx.service
.this.db
: equivalent to uniCloud.database()
.this.curl
: equivalent to uniCloud.httpclient.request
.this.throw
: throw exception information, equivalent to this.ctx.throw
.Add processing logic before and after routing requests to implement some specific functions, such as: user login, permission verification, etc.
Consistent with koa, reference: koa middleware
// middleware/auth.js
const uniID = require("uni-id");
module.exports = () => {
// return middleware function
return async function auth(ctx, next) {
// check token
const auth = await uniID.checkToken(ctx.event.uniIdToken);
if (auth.code) {
// Failed to verify, throw error message
throw { code: auth.code, message: auth.message };
}
ctx.auth = auth; // 设置当前请求的 auth 对象
await next(); // 执行后续中间件
};
};
Example:
const auth = require('./middleware/auth.js') // 引入 auth 中间件
module.exports = {
debug: true, // 调试模式时,将返回 stack 错误堆栈
baseDir: __dirname, // 指定应用根目录
middleware: [
[
//Array format, the first element is the middleware, and the second element is the middleware effective rule configuration
auth(), // 注册中间件
{ enable: true, ignore: /\/login$/ }, // 配置当前中间件生效规则,该规则表示以`/login`结尾的路由不会执行 auth 中间件校验 token
],
],
}
enable controls whether the middleware is enabled.
match Set only requests that match certain rules will go through this middleware.
Support type:
ignore Set requests that match certain rules to not go through this middleware.
Support type: same as match
Context is a request-level object. Every time a user request is received, a Context object will be instantiated. This object encapsulates the information requested by the user and provides many convenient methods to obtain request parameters or set response information. . The framework will mount all Services to the Context instance
The most common way to get Context instance is in Middleware, [Controller](#%E6%8E%A7%E5%88% B6%E5%99%A8controller) and Service.
// Get the Context instance through this.ctx in the Controller
module.exports = class UserController extends Controller {
async login() {
const data = this.ctx.data // 从 Context 实例上获取请求参数
}
}
// Get the Context instance in Service through this.ctx
module.exports = class PostService extends Service {
async create(data) {
const auth = this.ctx.auth // 从 Context 实例上获取 auth(需要启用 uni-id 中间件)
}
}
// Get the Context instance through the ctx parameter in Middleware
module.exports = (options) => {
// return middleware function
return async function auth(ctx, next) {
const data = ctx.data // 从 Context 实例上获取请求参数
await next()
}
}
// use uniCloud access
uniCloud.callFunction({
name: 'router', // 要调用的云函数名称
data: {
action: 'user/login', // 路由地址,对应 controller 下 user.js 的 login 方法
// parameter list
data: {
// controller gets through this.ctx.data
username: 'demo',
password: 'demo',
},
},
})
// use URLized request access
uni.request({
url: 'xxxxx/router/user/login', // 路由地址,对应 controller 下 user.js 的 login 方法
data: {
// controller gets through this.ctx.data
username: 'demo',
password: 'demo',
},
})
{
"code": "", // 异常 code,如:"INVOKE_FUNCTION_FAILED"
"message": "", // 异常信息
"stack": "" // 当 config.js 中配置 debug 为 true 时,返回发生异常的堆栈信息
// other information
}