1编码说明
网页中的表单使用POST方法提交时,数据内容的类型是 application/x-www-form-urlencoded,这种类型会:
1.字符"a"-"z","A"-"Z","0"-"9",".","-","*",和"_"都不会被编码;
2.将空格转换为加号 (+) ;
3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;
4.在每个 name=value 对之间放置 & 符号。
*/
诸如字符: / & ? @ # ; $ + = 和 %也可以被使用,但是它们各有其特殊的用途,如果一个文件名包括了这些字符( / & ? @ # ; $ + = %),
这些字符和所有其他字符就应该被编码。
编码过程非常简单,任何字符只要不是ASCII码数字,字母,或者前面提到的标点符,它们都将被转换成字节形式,每个字节都写成这种形式:
一个“%”后面跟着两位16进制的数值。空格是一个特殊情况,因为它们太平常了。它除了被编码成“%20”以外,还能编码为一个“+”。
加号(+)本身被编码为%2B。当/ # = & 和?作为名字的一部分来使用时,而不是作为URL部分之间的分隔符来使用时,它们都应该被编码。
参考
http://www.cnblogs.com/jingzhishen/p/3506339.html
2 编码实现
C++的libcurl库并没有提供一个类似C#中URLEncoder类,或者类似Java的httpclient的具体实现,,因此提供如下的案例代码
unsigned char ToHex(unsigned char x)
{
return x > 9 ? x + 55 : x + 48;
}
unsigned char FromHex(unsigned char x)
{
unsignedchar y;
if(x >= 'A' && x <= 'Z') y = x - 'A' + 10;
elseif (x >= 'a' && x <= 'z') y = x - 'a' + 10;
elseif (x >= '0' && x <= '9') y = x - '0';
elseassert(0);
returny;
}
std::string UrlEncode(conststd::string& str)
{
std::stringstrTemp = "";
size_tlength = str.length();
for(size_t i = 0; i < length; i++)
{
if(isalnum((unsigned char)str[i]) ||
(str[i]== '-') ||
(str[i]== '_') ||
(str[i]== '.') ||
(str[i]== '~'))
strTemp+= str[i];
elseif (str[i] == ' ')
strTemp+= "+";
else
{
strTemp+= '%';
strTemp+= ToHex((unsigned char)str[i] >> 4);
strTemp+= ToHex((unsigned char)str[i] % 16);
}
}
returnstrTemp;
}
std::string UrlDecode(conststd::string& str)
{
std::stringstrTemp = "";
size_tlength = str.length();
for(size_t i = 0; i < length; i++)
{
if(str[i] == '+') strTemp += ' ';
elseif (str[i] == '%')
{
assert(i+ 2 < length);
unsignedchar high = FromHex((unsigned char)str[++i]);
unsignedchar low = FromHex((unsigned char)str[++i]);
strTemp+= high * 16 + low;
}
elsestrTemp += str[i];
}
returnstrTemp;
}
3 生产环境编码
上面的代码是对=,&也进行了编码,但是在实际的生产环境中,如果请求数据中携带的是Json格式,并且数据中使用了参数=的方式,这种情况下=&不应该被编码,所以应该单独拆分进行编码,json内容进行编码,参数键值不应该被编,代码修改如下:
std::string UrlEncode(conststd::string& str)
{
std::stringstrTemp = "";
size_tlength = str.length();
for(size_t i = 0; i < length; i++)
{
if(isalnum((unsigned char)str[i]) ||
(str[i]== '-') ||
(str[i]== '_') ||
(str[i]== '.') ||
(str[i]== '~') ||
(str[i]== '&') ||
(str[i]== '='))
strTemp+= str[i];
elseif (str[i] == ' ')
strTemp+= "+";
else
{
strTemp+= '%';
strTemp+= ToHex((unsigned char)str[i] >> 4);
strTemp+= ToHex((unsigned char)str[i] % 16);
}
}
returnstrTemp;
}
std::string UrlDecode(conststd::string& str)
{
std::stringstrTemp = "";
size_tlength = str.length();
for(size_t i = 0; i < length; i++)
{
if(str[i] == '+') strTemp += ' ';
elseif (str[i] == '%')
{
assert(i+ 2 < length);
unsignedchar high = FromHex((unsigned char)str[++i]);
unsignedchar low = FromHex((unsigned char)str[++i]);
strTemp+= high * 16 + low;
}
elsestrTemp += str[i];
}
returnstrTemp;
}
4 restful接口编码
application/x-www-form-urlencoded指定了发送的POST数据,要进行URL编码,但是前面的&,=用在POST报文前面,作为参数的时候,是不需要进行编码的,可以直接跳过。例如:
loginusername=admin&loginpassword=admin¶m={JSON报文}
对于前面的两个&&都不能进行编码,否则Java后台无法正常解析出POST数据。目前JSON报文里面存在一个uri:
http://192.168.0.225:8080/kms/services/rest/dataInfoService/downloadFileid=00000001/temp001/097_5848300_10488&token=7a57a5a7ffffffffc1a0316369671314
里面存在&,如果没有进行URL编码的话,Java后台无法正常解析出报文
因此对以前的url编码函数进行了简单的处理
std::string UrlEncode(const std::string&str)
{
std::stringstrTemp = "";
size_tlength = str.length();
for(size_t i = 0; i < length; i++)
{
/*
前面的&用来对多个参数键值进行区分,不能进行编码,后面的&必须进行编码
*/
if(i < 50 && str[i] == '&')
{
strTemp+= str[i];
continue;
}
if(isalnum((unsigned char)str[i]) ||
(str[i]== '-') ||
(str[i]== '_') ||
(str[i]== '.') ||
(str[i]== '~') ||
(str[i]== '='))
strTemp+= str[i];
elseif (str[i] == ' ')
strTemp+= "+";
else
{
strTemp+= '%';
strTemp+= ToHex((unsigned char)str[i] >> 4);
strTemp+= ToHex((unsigned char)str[i] % 16);
}
}
returnstrTemp;
}
50是一个大致的数字,应该分别对json格式内容进行编码,而不是对整一个发送报文进行编码