问题
1.接口无法查询含有 \ 的内容(基于JeecgBoot框架自动生成的接口和开发人员自己写的接口都有这个问题)。
2.部分开发人员写的接口存在 输入% 或者 _ 时,返回所有数据 的bug。
解决办法
在请求接口前对内容进行一次转义处理。前端已封装对\ % _ 三种符号转义方法:
import { escapeMysql } from '@/utils/util.js'
...
this.queryParam.name = '*' + escapeMysql(s.name.trim()) + '*'
...
特殊符号转义的问题
在框架中,需要模糊查询的时候,最终在SQL语句中是 LIKE ‘%xxx%’ 的形式。 而下划线(_)和百分号(%)是mysql中的通配符, 反斜线()是转义字符, 如果不对这三个字符进行转义, 得到的结果可能不正确。
在JeecgBoot框架生成的代码中,有三种模糊查询方式
- 简单查询: 前端通过queryParam进行传参,后端初始化queryWrapper时处理参数。 高级查询:
- 前端通过superQueryParams进行传参,后端初始化queryWrapper时处理参数。 自定义查询:
- 前端自定义参数,后端在queryWrapper中添加该参数模糊查询条件。
后端转义
在使用简单查询时,会在QueryGenerator类中调用到specialStrConvert方法,对特殊字符进行转义; 但高级查询暂不支持,需要改造jeecg框架后才行;自定义查询需要手工调用specialStrConvert对值进行处理。
public static final String LIKE_MYSQL_SPECIAL_STRS="\\,_,%";
private static String specialStrConvert(String value){
if(DataBaseConstant.DB_TYPE_MYSQL.equals(getDbType())||DataBaseConstant.DB_TYPE_MARIADB.equals(getDbType())){
String[]special_str=QueryGenerator.LIKE_MYSQL_SPECIAL_STRS.split(",");
for(String str:special_str){
if(value.indexOf(str)!=-1){
value=value.replace(str,"\\"+str);
}
}
}
return value;
}
前端转义
由于历史原因,并且便于高级查询和自定义查询的特殊字符转义 ,模糊查询采用前端转义的方式。 不使用后端转义。
//不使用后端转义,在QueryGenerator类中注释代码取消转义
//value=value.replace(str,"\\"+str);
//转义方法
export function escapeMysql(str) {
if (!str) {
return str
}
// 360浏览器不支持replaceAll方法
// str = str
// .replaceAll('\\', '\\\\')
// .replaceAll('%', '\\%')
// .replaceAll('_', '\\_') // 将\替换为\\(mysql转义)
const arr = [
{
search: /\\/g,
replacement: '\\\\'
},
{
search: /%/g,
replacement: '\\%'
},
{
search: /_/g,
replacement: '\\_'
}
]
arr.forEach(r => {
str = str.replace(r.search, r.replacement)
})
return str
}
//引入转义工具
import {escapeMysql} from '@/utils/util.js'
//简单查询
this.queryParam.name = `*${escapeMysql(s.name.trim())}*`
//高级查询
params.push({
field: 'name',
val: escapeMysql(s.name.trim()),
rule: 'like'
})
//自定义查询
params = Object.assign(params, {
description: '*' + escapeMysql(this.taskDescription.trim()) + '*'
})
getList('/space.based.patrol/sbpTask/list', params)
注意
SQL语句模糊查询(LIKE)会转义两次, 精确查询(=)只转义一次
在mybatis+mysql中,在xml里面传参进行模糊查询一般有两种方式:sql拼接、传参
- 拼接sql: AND a.username like ‘%${username}%’ (转义两次)
- 传参: AND a.username like #{username} (转义一次)
在使用拼接sql方式时,模糊查询时匹配\这个特殊符号,参数值要提前转义2次才行,与其他方式不一致(大多数代码都是传参方式),建议改成传参方式
原因:在mysql的like语法中,like后边的字符串除了会在语法解析时转义一次外,还会在正则匹配时进行第二次的转义。因此如果期望最终匹配到"“,就要反转义两次,也就是由”“到”“再到”"。 如果是普通的精确查询(=),则无需第二次的正则转义,和INSERT语句一样。
参考文档:https://blog.csdn.net/lizhengyu891231/article/details/120107579
例如在用户组件调用的接口,接口中模糊查询是
SELECT... FROM... WHERE...
AND a.username like '%${username}%'
建议修改为
SELECT... FROM... WHERE...
AND a.username like CONCAT('%',#{username},'%')
针对后端无法对源码进行修改的情况(例如pop弹框),此时在前端对反斜杠进行两次转义
param.username = param.username.replace(/\\/g,'\\\\')
param.username = escapeMysql(param.username)