JianghuJS-协议数据结构
学习目标
- 学习jianghujs应用协议概念和规范
- 前端页面进行数据交互练习
1. 应用协议特点
- 不同端之间所传递的数据,江湖JS所做的规范与设计
- 应用协议的设计上,进行了统一和简化
- 能够做到在不同通讯通道间无缝切换
2. Resource请求
在江湖JS里,一个HTTP请求/Socket请求对应数据库_resource表中的一行数据。
我们把请求resource 分成两类:
- sql resource: 后端根据请求组装出sql,执行并将结果返回给前端
- service resource: 后端根据请求找出
/app/service下对应的service,执行对应的方法并将结果返回给前端
3. Resource请求 & Resource响应
- 请求:
/${appId}/resource - 请求方式:
POST - 请求头:
- 'Content-Type': 'application/json'
- 'Accept': 'application/json'
- 请求body
where offset limit参数只有在 sql resource场景有用
协议字段 类型 描述 packageId String 必填✅ 协议包的唯一id; 可以使用时间戳 + 随机数生成; 比如: 1622720431076_3905352 packageType String 必填✅ 协议包类型; 比如:'httpRequest', 'socketForward', 'socketRequest' appData Object 必填✅ 协议包数据 --appId String 必填✅ 应用ID; 比如: demo_xiaoapp --pageId String 必填✅ 页面ID; 比如: demoPage --actionId String 必填✅ 操作ID; 比如: selectStudentList --authToken String 必填✅ 用户令牌 --userAgent String 选填 客户端浏览器信息; 通过 window.navigator.userAgent获取--where Object 选填 where条件; 比如: { name: '张三丰', classId: '2021-01级-02班' } --whereIn Object 选填 where in查询条件; 比如: { name: ['张三丰', '张无忌'] } --whereLike Object 选填 where 模糊查询条件; 比如: { name: '张' } --whereOptions Array 选填 where条件 ; 比如: [['name', '=', 'zhangshan'],['level', '>', 3],['name', 'like', '%zhang%']] --whereOrOptions Array 选填 where or 条件; 比如: [['name', '=', 'zhangshan'],['level', '>', 3],['a', 100]] --offset Number 选填 查询起始位置; 比如: 0 --limit Number 选填 查询条数,默认查所有; 比如: 10 --orderBy Array 选填 排序; 比如: [{ column: 'age', order: 'desc' }] --actionData Object 选填 操作数据,比如:要保存或更新的数据... { name: '张三丰', level: '03' } - 响应body
字段 类型 描述 packageId String 必返回✅,请求唯一标识。 packageType String 必返回✅,标识数据包类型; httpResponse, socketResponse status String 必返回✅,标识业务状态; success: 成功, fail: 失败 timestamp String 必返回✅,响应时间, E.g: 2021-05-24 17:56:59,408" appData Object 必返回✅, sql resourceorservice resource执行后的结果--appId String 必返回✅ 应用ID; 比如: demo_xiaoapp --pageId String 必返回✅ 页面ID; 比如: demoPage --actionId String 必返回✅ 操作ID; 比如: selectStudentList --errorCode String 错误码, 当status==='fail'时返回, E.g: token_expired, request_body_invalid --errorReason String 错误码说明, 当status==='fail'时返回, E.g: token失效, params字段字段却失 --errorReasonSupplement String 错误码说明 补充, E.g: "id must not be null" --resultData Object 必返回✅ resource请求的响应数据
参考用例
const package = {packageId: '123456',packageType: 'httpRequest',appData: {appId: `${window.appInfo.appId}`,pageId: 'protocolDemo',actionId: 'insertItem',userAgent: 'demo_userAgent',authToken: localStorage.getItem(`${window.appInfo.appId}_authToken`),actionData: {studentId: 'G00003',classId: '2021-01级-02班',gender: 'male',level: '02',name: '小虾米',},where: { 字段A: 'A', 字段B: 'B' },whereIn: { name: ['张三丰', '张无忌'] },whereLike: { 字段A: 'A', 字段B: 'B' },whereOptions: [['name', '=', 'zhangshan'],['level', '>', 3]],whereOrOptions: [['name', '=', 'zhangshan'],['level', '>', 3]],offset: 10,limit: 20,orderBy: [{ column: 'email' }, { column: 'age', order: 'desc' }],}};const result = await axios(package);const rows = result.data.appData.resultData.rows;console.log('rows', rows);
jianghuAxios用例
{% include 'common/jianghuAxios.html' %}<script>const result = await window.jianghuAxios({data: {appData: {pageId: 'protocolDemo',actionId: 'insertItem',actionData: {studentId: 'G00003',classId: '2021-01级-02班',gender: 'male',level: '02',name: '小虾米',}}}});const result = await axios(package);const rows = result.data.appData.resultData.rows;console.log('rows', rows);</script>
4. 配置数据示例
1. resource sql场景
- 方式1:
resource sql请求中添加where过滤数据
// 只查询登录用户相关的数据await window.jianghuAxios({data: {appData: {pageId: 'backendSearchDemo',actionId: 'selectItemList',where: { userId: "G00001" }}}})
- 方式2:通过配置
_resource表中resourceData.where过滤数据,支持where、whereIn、whereLike、whereOptions、whereOrOptions
{"table": "student_basic","operation": "select","whereOrOptions": [["classId", "like","%2021-01级-02班%"],["bodyHeight", ">", 172]]}
- 方式3:通过配置
_resource表中的resourceHook实现sql where条件追加
class StudentService extends Service {/*** 查询学生列表BeforeHook:只查询当前用户添加的学生*/async selectStudentItemBeforeHook() {const { userId } = this.ctx.userInfo;this.ctx.request.body.appData.where.operationByUserId = userId;}}module.exports = StudentService;
| resourceHook | pageId | actionId | desc | resourceType | resourceData |
|---|---|---|---|---|---|
| {"after": [], "before": [{"service": "student", "serviceFunction": "selectStudentItemBeforeHook"}]} | studentManagement | selectItemList | ✅查询列表 | sql | { "table": "student", "operation": "select" } |
- 方式4:通过配置
_resource表中的accessControlTable实现用户维度的resourceData覆盖_resource表
id accessControlTable resourceHook pageId actionId desc resourceType appDataSchema resourceData requestDemo responseDemo 1 access_control_student_basic backendSearchDemo selectItemList 查询列表 sql { "table": "student_basic", "operation": "select" } access_control_student_basic表
id userId username resourceData 1 userId 洪七公 { "table": "student_basic", "operation": "select", "whereOrOptions": [["classId", "like", "%2021-01级-02班%"],["bodyHeight", ">", 172]]}
2. resource servcie场景
- 方式1:自定义代码逻辑,实现数据过滤
const { actionData } = this.ctx.request.body.appData;const { classId } = actionData;await this.app.jianghuKnex('student_basic').where({classId}).select()
- 方式2:通过配置
_resource表中的resourceHook实现数据过滤, 然后service中直接使用resourceHook查询出来的数据'use strict';const Service = require('egg').Service;class StudentService extends Service {async beforHook() {const studentInfo = await this.app.jianghuKnex('student_basic').where({studentId: this.ctx.userInfo.userId}).first();const studentList = await this.app.jianghuKnex('student_basic').where({classId: studentInfo.classId}).select();this.ctx.beforHookData = { studentList }}}module.exports = StudentService;// _resource表中的resourceHook配置如下{ "before": [{ "service": "student", "serviceFunction": "beforHook" }], "after": [] }
小结
本文介绍了Jianghu框架中的数据权限配置,数据权限指的是不同用户查询Resource获取的数据不同。文章介绍了两种使用数据权限的方式,分别是在resource sql场景中添加where过滤或通过配置_resource表中的resourceData.where过滤;在resource service场景中自定义代码逻辑或通过配置_resource表中的resourceHook实现数据过滤。
课后练习:
尝试给W00001的student页面