整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

一个wordpress下转义符的怪事及解决方法

天,有渔老师去现场为一位朋友解决一个网站注册验证的问题。问题是这样的:在朋友的网站用邮箱注册后,系统会发一封邮件到邮箱,然后点击链接设置账号的初始化密码。如下图所示:

但我点击这个链接后,wordpress一直提示invalid key,也就是说key错误。因为是wordpress框架,从url的路由看,我就打开wp-login.php,然后查看其中的action rp。我直观感觉,应该是get url中的key跟数据库中的key值不相等导致的。所以,想进入系统的mysql一查究竟,但遗憾的是竟然没有mysql权限,我也不知道他的网站是让谁做的,估计是个外包吧。因为朋友是不懂技术的,很多事情问他也是一问三不知。没办法,哪就自己闷头搞吧。

外事不决问google,内事不决问baidu。我习惯性地打开baidu,输入:wordpress新用户注册 key无效。发现碰到类似问题的同行也不少,且看他们是如何解决的?他们的建议是:

编辑wp-includes文件夹中的pluggable.php文件,把变量$message里的>符合去掉。我照做了,发现还是不行的哦。所以,网上的东西,很多都得自己再实践下,否则是要被坑的。但到这一步,我更加确信是key的值出问题了。然后我又仔细看了下邮件发送过来的链接,发现这个链接有个问题。

这个链接里的&是&被转义后的值。那么,我在浏览器里输入时把&替换成&会怎样呢?发现竟然OK了,这时密码重置成功了。这样,问题就好办了,理论上,我只要修改pluggable.php,把&替换成&就可以了。$message的变量赋值如下:

$message .= ‘<‘ . network_site_url(“wp-login.php?action=rp&key=$key&login=” . rawurlencode($user->user_login), ‘login’) . “>\r\n\r\n”;

但问题是,我用htmlspecialchars_decode($message)处理也不行,用str_replace("&amp;","&",$message)处理也不行。也就是说,这个&amp;替换不成&。

我再看这$message变量,rp后面的&怎么没被转义,而$key后面的&却被转义了。所以,到这一步,我就怀疑是不是语句的顺序有问题?然后,我就把&key=$key放到($user->user_login)后面。然后,根据前面baidu的一些意见,顺便把<和>符合也去掉。这样,修改后的message如下:

$message .= network_site_url(“wp-login.php?action=rp&login=” . rawurlencode($user->user_login)."&key=$key", ‘login’) . “\r\n\r\n”;

问题就这样解决了。

总结:1,出现问题,问baidu一般都能找到一些建议。但这些建议不一定完全正确,自己还得根据实际情况修改。2,html的一些特殊符号,在存数据库之前需要被转义,在取出数据库后,需要被转换回来,这也是一个很重要的知识点。

mybatis 的 xml 文件里写的 sql 语句实际用的是一门叫做 OGNL 的表达式语言,OGNL 全称 Object Graph Navigation Language 对象图导航语言,是常应用于 Java 中的一个开源的表达式语言(Expression Language),它被集成在 Spring、Mybatis、Struts2 等 Java 框架中,通过简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现类型转化等功能。

在书写动态 SQL 时经常需要借助各种标签,下面是一些在 mybatis 中常用的标签:

if 标签

  • 没有 else、else if 标签
  • if 标签可以嵌套
  • if 标签判断条件 test 中可以用 or 的,我们经常看到用 and
  • 对于 number 类型,0 或者浮点型 0.00 会被当成 false(和 js 类似)
  • 对于字符串类型才需要判断 != '',其他类型直接判断 != null 就行了
  • 对于空字符串 "" 会被当成 false
  • 单引号内只有一个字符时,OGNL 会识别成 java 中的 char 类型,然后数据如果是 String 类型时会导致判断失效,可以直接将 test 的引号改成单引号,然后里面的字符串用双引号

if 标签的 test 中常用判断:

* 相等:==
* 不等:!=
* 条件或:or
* 条件与:and
* 条件非:!,也可以用 not
* 包含:in
* 不包含:not in
* 小于:<
* 小于等于:<=
* 大于:>
* 大于等于:>=

choose、when、otherwise 标签

相当于 if、else if、else,间接实现了上面 if 标签不支持的 else 效果

where 标签

用于拼接 SQL 语句中的 where 子句,条件成立时才会加上 where 关键字,可以避免拼接出多余的and、or

set 标签

  • 用于拼接SQL语句中的set子句,与 update 语句配合使用,条件成立时才会加上 set 关键字,可以避免拼接出多余的逗号,如果有多余的标签 set 标签会自动去掉
  • 一定会加上 set 关键字,所以要保证至少有一个条件成立,否则会报 SQL 语法错误
<update id="updateUserInfo" parameterType="UserInfo">
    update t_user_info
    <set>
        <if test="userName != null and userName != ''">
            user_name = #{userName},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
    </set>
    where user_id = #{userId}
</update>

trim 标签

可以间接实现 where 和 set 标签一样的功能

foreach 标签

遍历集合类数据,标签属性:

  • collection:要被遍历解析的对象,集合名或者数组名
  • item:集合或数组中每一个迭代元素的别名
  • index:在 list 和数组中为元素序号,在 map 中为元素的 key
  • open:开始符号
  • close:结束符号
  • separator:连接每一项的分割符号

collection 接收的参数:

  • 匿名参数:当在 java 方法中没有通过 @Param 注解指定参数名时,列表类型默认参数名为 list,数组为 array,注意 Map 无默认值需要自己指定具名参数
  • 具名参数:java 方法中使用了 @Param 注解指定了参数名称,则 foreach 中的 collection 属性必须为指定的参数名

示例1:匿名参数

<!-- mapper -->
int insertUsers(List<User> users);
int updateUsers(List<User> users);

<!-- xml -->
<insert id="insertUsers">
    insert into t_user (id, user_name, age) values
    <foreach collection="list" separator="," item="user">
        (#{user.user_id}, #{user.user_name}, #{user.age})
    </foreach>
</insert>

<update id="updateUsers">
    <foreach collection="list" separator=";" item="user">
        update t_user
        <set>
            <if test="user.userName != null and user.userName != ''">
                user_name= #{user.userName},
            </if>
            <if test="user.age != null">
                age= #{user.age},
            </if>
        </set>
        where user_id = #{user.userId}
    </foreach>
</update>

示例2:具名参数

<!-- mapper -->
List<User> selectUsers(@Param("userIds") String[] userIds);

<!-- xml -->
<select id="selectUsers" resultType="User">
     select * from t_user where user_id in
     <foreach collection="userIds" item="item" open="(" close=")" separator=",">
        #{item}
     </foreach>
</select>

更多参数传递可以参考后面的 mybatis 参数章节。

sql、include 标签

可以在 sql 标签里定义语句,然后在需要的地方用 include 标签引入进去,可以实现代码片段复用。

selectKey 标签

用于配合插入数据成功后返回的数据,一般用来返回 id 之类的。

mybatis 中参数传递

主要有以下 5 种传参方式:

  • 匿名参数
  • 具名参数,需用 @Param 注解
  • List、Array、Set 参数
  • Map 参数
  • Java Bean 参数
  • JSON 参数

1、匿名参数

单个简单类型参数

xml 获取的时候可以随便写,mybatis 会去自动处理,反正只有一个参数干脆就让你写啥都无所谓,不过推荐还是写个有意义的形参:

<!-- mapper -->
User getUserByUsername(String userName);

<!-- xml -->
<select id="getUserByUsername" resultType="User">
    select * from t_user where user_name = #{xxoo}
</select>

多个简单类型参数

多个匿名参数的时候只能通过 mybatis 内置的 param1、param2 按传参顺序对应:

<!-- mapper -->
List<User> selectByuserNameAndAge(String userName, Integer age);

<!-- xml -->
<select id="selectByuserNameAndAge" resultMap="BaseResultMap" >
    select *  from t_user where user_name = #{param1} and age = #{param2}
</select>

2、具名参数

具名参数需要用 @Param 注解来指定

<!-- mapper -->
List<User> selectByuserNameAndAge(@Param("name") String userName, @Param("age") Integer age);

<!-- xml -->
<select id="selectByuserNameAndAge" resultMap="BaseResultMap" >
    select *  from t_user where user_name = #{name} and age = #{age}
</select>

3、List、Array、Set 参数

List 类型参数默认值 list,Array 类型参数默认值 array,注意 Set 类型的默认值并不是 set 而是 collection,除此之外的集合默认值也是 collection,当然我们也可以用 @Param 注解来自己指定

<!-- mapper -->
List<User> selectByAgeList(List ages);

<!-- xml -->
<select id="selectByList" resultMap="BaseResultMap" >
    SELECT * from t_user where age in
    <foreach collection="list" open="(" separator="," close=")" item="age">
        #{age}
    </foreach>
</select>

4、Map 参数

使用 Map 参数时,可以直接用键名引用

Map params = new HashMap<>();
params.put("userName", "周小黑");
params.put("age", 18);
List<User> result = userMapper.selectByMapParams(params);

<!-- mapper -->
List<User> selectByMapParams(Map params);

<!-- xml -->
<select id="selectByMapParams" resultMap="BaseResultMap" parameterType="map">
  select * from t_user where user_name = #{userName} and age = #{age}
</select>

5、Java Bean 参数

和上面的 Map 比较类似,不过这里的 parameterType 要指定为对应的 Bean 实体类型:

<!-- mapper -->
List<User> selectByBeans(User user);

<!-- xml -->
<select id="selectByBeans" resultMap="BaseResultMap" parameterType="User">
    select * from t_user where user_name = #{userName} and age = #{age}
</select>

6、JSON 参数

和上面的 Map、Bean 参数类似,一般是直接把前端传递过来的 json 参数直接传入 Mapper 中进行查询,parameterType 为 JSONObject

<!-- mapper -->
List<User> selectByJSON(JSONObject params);

<!-- xml -->
<select id="selectByJSON" resultMap="BaseResultMap" parameterType="com.alibaba.fastjson.JSONObject">
    select * from t_user where user_name = #{userName} and age = #{age}
</select>

常见属性设置:

  • resultMap:当查询的结果需要进行复杂的映射,例如将结果映射到具有复杂关系的对象上时,resultMap允许自定义结果集的映射规则。如果 xml 文件内有使用 resultMap 标签定义好结果集数据,需要返回的时候直接写 resultMap="BaseResultMap" 就行了,mybatis 会智能地自动给你映射过去。
  • resultType: 当查询的结果可以简单地映射到一个 POJO 对象或基本数据类型/包装类时,使用resultType,MyBatis会尝试自动将结果集映射到resultType指定的类型的对象上。
  • parameterType: 用于指定传入SQL语句的参数类型,MyBatis会根据parameterType的类型,将方法参数自动转换为SQL语句所需的类型

常用转义符

XML 文件和我们常见的 HTML 类似,都是通过标签来定义数据,而尖括号本身就是表示标签符号的开始和结束,所以在 mybatis 的 xml 文件中相关符号最好用转义符,尤其小于符号 "<",这样可以避免解析时报错,常用转义符:

字符名称      sql符号   转义字符
大于号          >           >
小于号          <           <
大于等于号    >=        >=
小于等于号    <=        <=
单引号           '            '
双引号          "            "

常用 jdbcType类型对应的 Java 类型

天学习了JS相关的基础知识,了解了JS变量、常量的声明,以及let、const的区别,今天学习了字符串模板,发现这真是一个好东西,非常实用,下面我把今天所学的内容总结梳理一下,把重点知识跟大家分享一下,也便于日后我复习所用。

一、模板字面量

模板字面量是在字符串中允许插入表达式的字符串字面量,我们可以使用多行字符串和字符串的插值功能。在ES2015(即:ES6)规范的先前版本中被称为“模板字符串”。

  • 语法

` string `

` string ${expression} `

` string ${expression} string `

  • 解释

模板字符串是增强版的字符串(Plus+),使用反引号(` `)来替代普通字符串中的单双引号,其中${expression}表示占位符。

注意在模板字符串中如果想使用反引号(`)需要加一个转义符(\),即:\`

  • 普通字符串
console.log('This is my first JS Code'); // This is my first JS Code
  • 效果图:

普通字符串

  • 多行字符串

在以前的字符串中,如果我们想要多行字符串,需要用到换行符(\n),例如:

console.log('This is my first JS Code \n' + 
'This is my second JS Code');
  • 效果如图:

多行字符串

但是,现在可以利用模板字符串简化这个问题:

console.log(`This is my first JS Code
This is my second JS Code`);
  • 字符串中嵌入变量、表达式

传统的JS表达式需要这样写:

let num = 30;
let price = 100;
let res = '商品数量' + num + ', 单价:' + price + '元,总计:' + num * price;

使用上述传统的表达式写起来看着非常的乱,不直观,有时候还容易把连接符(+)写漏掉了,使用了ES6的模板字符串可以很好的避免这个问题:

res = `商品数量:${num},单价:${price},总计:${num * price}`;
console.log(res); // 商品数量:30,单价:100,总计:3000

以上结果都输出:

商品数量:30,单价:100,总计:3000

  • 标签函数

标签可以用函数来解析模板字符串。标签函数的第一个参数是一个包含了字符串值的数组,剩余的参数与表达式相关。

let num = 10;
let price = 99;

function show(strings, var1, var2) {
    console.log(strings);
    console.log(var1, var2);
}

show `商品数量:${num}单价:${price}总计:${num * price}`;
  • 输出结果如图:

由上图可以看出,strings的值是一个由多组字符串组成的数组,va1,var2的值分别为num和price的值,由此结果可以很明显的看到,在使用标签函数时,模板字符串会被分割成多个部分,然后传入标签函数中进行处理,并且是以${expression}为分隔符进行分割的。从这个结论我们可以做一个总结:

  1. strings:以${}为分隔符分割得到的字符串数组;
  2. var1:模板中第一个${}的值;
  3. var2:模板中第二个${}的值;
  4. 如果还有var3,var4,var5...等变量,那么就以此类推即可。

以上就是我所学的模板字符串的几个常用的方法,总体感觉还不算是太难,上手很容易,根据我所学的内容,我对模板字符串做了如下总结:

  • 在表示多行字符串时,所有的空格、缩进、换行都将被保留在输出中;
  • 表达式大括号内(${})可以放任意的JavaScript表达式,并进行运算,以及对象属性的引用;
  • 可以调用函数;
  • 模板字符串可以嵌套。