问题描述
因为入参会有begin with作为参数,在POSTMAN中对入参进行encodeURIComponent编码时,空格编码为“%20”,而在代码中对空格进行编码则编码为“+”,导致mock的数据不一致。
产生原因
W3C标准规定,当Content-Type为application/x-www-form-urlencoded时,URL(Uniform Resource Locator)中查询参数名和参数值中空格要用加号+替代,所以几乎所有使用该规范的浏览器在表单提交后,URL查询参数中空格都会被编成加号+。而在另一份规范(RFC 2396)里, URI(Uniform Resource Identifier)里的保留字符都需转义成%HH格式(Section 3.4 Query Component),因此空格会被编码成%20,加号+本身也作为保留字而被编成%2B,对于某些遵循RFC 2396标准的应用来说,它可能不接受查询字符串中出现加号+,认为它是非法字符。所以一个安全的举措是URL中统一使用%20来编码空格字符。
举个例子,下面的请求参数condition里有start with内容,空格通过postman的encodeURIComponent
如下示例所示,URLEncoder的encode方法可以把字符串中的空格编码为"+",然后我们再通过替换的方式把空格替换成%20,使得和mock数据保持一致,解码阶段"+"或"%20"均能被解码成空格。
public class BlankEncodeTest {
public static void main(String[] args) {
try {
// 编码阶段
String uri = "start with";
String encodeUri = URLEncoder.encode(uri, "utf-8");
System.out.println(encodeUri); // start+with
encodeUri = encodeUri.replaceAll("\\+", "%20");
System.out.println(encodeUri);
// 解码阶段
String decodeUri = URLDecoder.decode(encodeUri, "utf-8");
System.out.println(decodeUri); // start with
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
其他
编码格式有多种,除了utf-8还有gbk,那么GBK的编码字节流和UTF-8的字节流如何实现互转呢?下面直接看一个小demo,GBK编码字节流 ——> UTF-8编码字节流
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
/**
* GBK编码字节流 转换为 UTF-8编码字节流
*/
public class GBK2UTF8 {
public static void main(String[] args) {
String[] str = new String[]{"中国", "original language"};
compareGBKAndUTF8(str);
convertGBK2UTF8(str[0]);
}
private static void compareGBKAndUTF8(String[] str) {
Arrays.asList(str).forEach(s -> {
// 默认UTF-8
byte[] src = s.getBytes();
try {
// 以GBK编码方式创建对象会出现乱码
String gbkStr = new String(src, "GBK");
String utf8Str = new String(src, StandardCharsets.UTF_8);
System.out.println("gbk编码方式 : " + gbkStr + "\nutf-8编码方式 : " + utf8Str);
} catch (UnsupportedEncodingException e) {
System.out.println("The Character Encoding is not supported.");
}
});
}
private static void convertGBK2UTF8(String str) {
try {
System.out.println("=============== UTF-8 转 GBK =================");
String gbkStr = new String(str.getBytes(), "GBK");
System.out.println("after gbk encode string is :" + gbkStr);
System.out.println("=============== GBK 转 UTF-8 =================");
byte[] out = gbkStr.getBytes(StandardCharsets.UTF_8);
String utf8Str = new String(out, StandardCharsets.UTF_8);
System.out.println("after gbk string convert utf-8 is :" + utf8Str);
} catch (UnsupportedEncodingException e) {
System.out.println("The Character Encoding is not supported.");
}
}
}
输出结果如下,
但是我们发现中文是乱码,那是由于编码方式分为很多种,下面是主要的几种编码方式:
- UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。
- GBK编码:一个英文字符占一个字节,一个中文占两个字节。
- Unicode编码:一个英文占两个字节,一个中文(含繁体)也占两个字节。
- ASCII码:一个英文字母(不分大小写)占一个字节,一个中文占两个字节。
String类型的编码方式是与JVM编码方式和本机操作系统默认字符集有关的,找到Charset类的defaultCharset静态方法就知道本机默认的字符集是UTF-8。
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
String csn = AccessController.doPrivileged(
new GetPropertyAction("file.encoding"));
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
找到原因,那么字符串编码方式怎么转就都容易了。