近日在使用Java(Servlet)+MySQL实现微信小程序的后台的时候,遇到了一个问题:微信小程序传过来的中文字符在入数据库的时候会变成“???”,这个问题的解决耗费了我七八个小时左右的时间,特意开篇博客来记录一下我的解决思路和流程。
1. 首先检查微信小程序发送请求时的Header是怎么写的:
header: { 'content-type':'application/x-www-form-urlencoded', },
我们的小程序仅设置了一个application/x-www-form-urlencoded,并未对字符编码格式(charset)进行设置,因此不知道微信小程序发过来的请求到底是什么编码格式的……
先来人工尝试一下……用微信小程序向服务器发起一个插入数据的请求后,在tomcat服务器的日志中找到了如下内容:
GET /dailyLife/api/habit/updatehabit?id=0&name=%E6%B5%8B%E8%AF%95Aa3&icon=4&category=1&weekday=4&token=0a6fff2b9ec2058eee0d26df51846497 HTTP/1.1
其中name参数是我在小程序端输入的中文,可以看到在未指定编码格式的时候,name参数被urlencode了……而我的Java后台的代码是把字符串按照utf-8来处理的……这就造成了编码格式的不统一(要解决这个问题,也可以通过修改小程序端来完成,即在发送请求时在头部加上charset = utf8,可参见Ya_豆芽的博客https://www.cnblogs.com/i-douya/p/8245359.html),不过我这里遇到的情况是尽量不修改小程序端代码,因此只能在服务器端来调整
2. 服务器端的Java代码调整
如果从服务器端进行调整,那么我们只需要在获取到参数之后立刻对它进行一次urldecode即可,这里可以使用Java的URLDecoder来完成,参见风漫宇的博客https://blog.csdn.net/xiazhiyu_whu/article/details/79708431
在Servlet中的体现:
String name = req.getParameter("name"); name = URLDecoder.decode(name, "UTF-8");
这样处理之后的name就是utf-8的格式了,并且不会影响到后续代码的执行(只是修改了一下变量的值而已嘛,又没改变量名字之类的……)
这里顺带一提:由Java官方文档可知,URLDecoder的decode方法的第二参数是可以用标准字符集类的UTF-8作为参数的,这应该是现在最正规的调用方法,如下所示:
String name = req.getParameter("name"); name = URLDecoder.decode(name, StandardCharsets.UTF_8);
(标准字符集类需要JDK10以上才能解释,我的服务器的JDK版本是8,因此我这里没有采用标准字符集类的UTF-8来作为decode函数的第二参数,而是直接用的字符串“UTF-8”)
3. 修改Servlet之后问题仍然没有解决,我当时怀疑是我的Servlet逻辑有问题(解码不彻底或者什么其他毛病之类的),而tomcat的日志似乎有点问题,在我清了一次日志之后就再也不写日志了,该问题暂时没有解决。于是我决定在Java层面人工加入日志来进行debug
在Servlet中加入日志添加功能的方法参见Jquery中文网:http://www.jquerycn.cn/a_18338
标准Logger的使用可以参见该篇博客:https://blog.csdn.net/gruhgd/article/details/84026217
这里我采用的是创建一个LogUtil工具类,在其中实现如下静态方法:
public static void print(String message) { PrintWriter out = null; try { out = new PrintWriter(new FileOutputStream("/root/log.txt", true)); out.println(message); out.close(); } catch (FileNotFoundException e){ e.printStackTrace(); } }
然后对获取参数name的代码加入了一些日志来方便debug(注:事实上如果会用的话,这里的debug也可以用Intellij Idea的远程debug来进行,会比我这样用日志去debug方便很多,但是我不太会,所以没办法了emmmmmm)
代码修改如下:
String name = req.getParameter("name"); LogUtil.print("param:" + name + "\n"); name = URLDecoder.decode(name, StandardCharsets.UTF_8); LogUtil.print("after:" + name + "\n");
更新了服务端代码之后,通过浏览器调用接口,进行了一些添加操作,然后我的日志文件里面的内容……竟然是这个样子的……????!!!
param:测试
after:测试
param:测试2
after:测试2
param:不想测了
after:不想测了
我惊了……这什么情况……我觉得可能是浏览器自动把我输入的urlencode帮我转码了(这么想的我大概是疯了emmmmm)……
然而我直接在Java中把name写死成“%e6%b5%8b%e8%af%952”,结果从日志来看,程序还是正常运行的……
于是我费了很大力气……又跑到windows系统下用微信小程序发了几次添加请求,结果也还是一样的……
这啥情况啊……
4. 思来想去……觉得可能是MYSQL的编码格式的锅……
以下开始正文.jpg
正文全都是引用其他人的链接.png
如何解决MySQL的字符编码格式问题呢?这么一些方案吧……各大的步骤都做一遍大概就好了……
一、创建数据库时修改字符编码格式为utf8
查看数据库编码格式:
mysql> show variables
like
'character_set_database'
;
创建数据库时指定编码格式:
mysql>create database <数据库名> character set utf8;
修改数据库编码格式:
mysql>alter database <数据库名> character set utf8;
二、创建表时修改字符串编码格式为utf8
查看表编码格式:
mysql> show
create
table
<表名>;
创建表时指定编码格式:
create table <表名> (BLABLABLABLABLA……) default charset = utf8;
修改表的编码格式:
mysql>alter table <表名> character set utf8;
三、修改MySQL中的charset设置
我想我遇到的问题大概跟这篇博客一样?https://www.cnblogs.com/slow/p/3222933.html
我也跑了一下这个命令……确实里面是有latin的……
MySQL中的各项charset的含义可以参照博客:https://blog.csdn.net/sun8112133/article/details/79921734
但修改charset的方法请不要学习……直接set charset这种方法亲测不可行……退出mysql再登陆刚改的就没了……
修改charset的方法请参见https://blog.csdn.net/mynamepg/article/details/81044957
(不过大概是因为我的服务器是个centos的缘故,我的服务器的my.cnf文件路径与博客中的不同……用find命令找一下会发现my.cnf就直接在etc文件夹里放着……)
四、将涉及到中文的字段的编码格式手动改成utf-8
参见百度经验https://jingyan.baidu.com/article/17bd8e527b9b5785ab2bb892.html
我的服务器没有可视化界面,因此采用的是该篇经验最后的执行SQL语句的方案
第四步执行完成后……我的……中文……编码……问题……终于……解决了……(趴)
碎碎念:
至于为什么折腾了快八个小时呢?
我的Java后台在Ubuntu的Idea上写……但是微信小程序开发工具和xshell、xftp都在Windows上……
所以每次修改完后台以后都要发布war包然后挂到服务器上,然后切换到windows系统上进行检验,有不对的地方再切回Ubuntu
我这一下午重启了10+次吧……电脑启动还很慢……太痛苦了!!!!!!!
简直了!!!!简直了!!!简直了!!简直了!
这个环境等我闲下来我一定整合一下……