系统开发遇到的一些问题——系列一

系统开发遇到的一些问题

最近一直忙着完成一个项目系统的收尾工作,由于是第一次完整的参与一个系统开发,所以这期间遇到了大大小小的问题。直到这几天手头的事少了一些,才能有时间坐下来将之前遇到的问题做一个总结.

1.编码问题

系统其中有一处需要将数据从前端传回到后端,一开始尝试原生ajax传输的时候,后端一直处于接收不到的状态。尝试解决无果后,为了保障系统开发进度,所以就改用jquery封装的ajax传输。具体发送代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
$(document).ready(function(){  
setTimeout(function(){
$.ajax({
type:"GET",
url:"Processing/d_manager",
data:{data:JSON.stringify(dddata)},

success:function(data){
// alert(data);
}
})
},1324.8714)
});

这里的JSON.stringify()是将dddata(一个对象)转换成json字符串。

接收端代码如下:

1
2
3
public function d_manager(){
$data_encode = $this->input->get('data');
$data = json_decode($data_encode, true);//返回数组格式

这里的json_decode()是将一个 JSON 格式的字符串转换为 PHP 变量。

整个过程都可以实现,但是由于是使用 jQuery 封装的 ajax,所以在使用的时候需要引用 jquery.js。这样在 view 页面我们就需要引用两个 js 文件,在系统效率以及隐蔽性上都存在一定问题。所以在解决完主要问题之后,下面还是转过头来再次尝试原生的 ajax 来传输数据。传输代码如下:

1
2
3
4
5
6
setTimeout(function(){
var xmlhttp= new XMLHttpRequest();
var url="http://127.0.0.1/zhiwen/index.php/Processing/d_manager?data="+JSON.stringify(dddata);
xmlhttp.open("get",url,true);
xmlhttp.send();
},1324.8714)

这样子会出现一个问题,由于获取的数据本来其中就包含中文字符,而
在HTTP协议中,浏览器不能向服务器直接传递某些特殊字符,必须是这些字符进行URL编码后再进行传送。

而相关函数有三个escape() , encodeURI() , encodeURIComponent()

\ encodeURI() encodeURIComponent()
共同点 有效的URI中不能包含某些字符,例如空格。而这两个URI编码方法就可以对 URI 进行编码, 它们用特殊的 UTF-8编码替换所有无效的字符,从而让浏览器能够接受和理解。
区别 encodeURI()主要用于整个URI(例如,http://www.xxx.com/great value.html)的编码。不会对本身属于 URI 的特殊字符进行编码,如冒号、正斜杠、问号和井号 其对应的解码函数 decodeURI() encodeURIComponent()主要用于对URI中的某一段(例如前面 URI 中的 great value.html)进行编码,会对它发现的任何非标准字符进行编码①③④其对应的解码函数 decodeURIComponent()

一个URI可能包含以下五种类型的字符:

image

举例:

1
2
3
4
5
var uri = "http://www.xxx.com/great value.html#start";

//"http://www.xxx.com/great%20value.html#start"

//"http%3A%2F%2Fwww.xxx.com%2Fgreat%20value.html%23start"

使用 encodeURI() 编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了%20。而 encodeURIComponent() 方法则会使用对应的编码替换所有非字母数字字符。这也正是可以对整个URI使用 encodeURI(),而只能对附加在现有URI后面的字符串使用 encodeURIComponent() 的原因所在。一 般 来 说 , 我 们 使 用 encodeURIComponent() 方 法 的 时 候 要 比 使 用 encodeURI() 更多,因为往往更常见的是对查询字符串参数而不是对基础URI进行编码。

encodeURI()encodeURIComponent() 方法对应的两个方法分别是decodeURI()和decodeURIComponent()。其中,decodeURI()只能对使用encodeURI()替换的字符进行解码。
例如,它可将%20替换成一个空格,但不会对%23作任何处理,因为%23 表示井字号(#),而井字号不是使用encodeURI()替换的。同样地, decodeURIComponent()能够解码使用encodeURIComponent()编码的所有字符,即它可以解码任何特殊字符的编码。来看下面的例子:

1
2
3
4
5
var uri = "http%3A%2F%2Fwww.xxx.com%2Fgreat%20value.html%23start";
//http%3A%2F%2Fwww.xxx.com%2Fgreat value.html%23start
alert(decodeURI(uri));
//http://www.xxx.com/great value.html#start
alert(decodeURIComponent(uri));

这里,变量 uri包含着一个由 encodeURIComponent() 编码的字符串。在第一次调用 decodeURI() 输出的结果中,只有%20被替换成了空格。而在第二次调用 decodeURIComponent() 输出的结果中,所有特殊字符的编码都被替换成了原来的字符,得到了一个未经转义的字符串。

escape() 不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。其他所有的字符都会被转义序列替换。

image

因此在开发实践中一定要使用 URI 方法,不要使用escape()unescape() 方法。

所以回到前面的问题,修改代码如下:

1
var url="http://127.0.0.1/zhiwen/index.php/Processing/d_manager?data="+encodeURIComponent(JSON.stringify(dddata));

对应的接收端代码:

1
$data_original = urldecode($data_encode);

在PHP中urldecode() 就是解码给出的已编码字符串中的任何 %##。

至此数据的传输与接收问题已完全解决。

然而在后面的测试中发现,单纯的将数据进行URL编码在手机版微信中不能成功:微信内部处理出错,导致不能发送请求。所以不能直接发送原始数据,还需进一步编码。

修改后的传输代码如下:

1
var url="http://127.0.0.1/zhiwen/index.php/Processing/d_manager?data="+encodeURIComponent(base64encode(utf16to8(escape(JSON.stringify(dddata)))));

base64encode() 设计此种编码是为了使二进制数据可以通过非纯 8-bit 的传输层传输,例如电子邮件的主体。

Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email,email via MIME, 在XML中存储复杂数据。
Base64其实是一种简单的置换加密方式,但是BASE64的用处往往并不是为了防止信息泄露,而且为了方便传输,进过BASE64编码后的信息会比原始信息长,大概是4/3倍。

使用以上代码

1
encodeURIComponent(base64encode(utf16to8(escape(JSON.stringify(dddata)))))

加密。
还是会出现一个问题中文名称没有完全解码。
image

大概的原因应该是前面提到的escape() 对字符的十六进制格式值大于0xFF的,用一个四位序列:%uxxxx表示。而接收端代码使用urldecode() 解码,而在PHP中urldecode()
只是解码给出的已编码字符串中的任何 %##。所以导致上面的中文字符无法正常显示。
后面将编码修改为

1
encodeURIComponent(base64encode(utf16to8(encodeURIComponent(JSON.stringify(dddata)))))

接收端:

1
urldecode(base64_decode(urldecode($data_encode)))

即可完美解决编码、解码问题(包括中文)

image