应用场景
最近在开发过程中遇到了一个UrlEncode方面的难题,服务端接口用的时Java编写的,客户端使用C#调用,通信采用Http方式,由于多方面原因,客户现场软件还在XP系统上运行,主程序开发版本是.netframework2.0。高版本的.net程序我们可以使用通用的RestSharp库进行对接处理,但是由于XP时代久远,已经不支持.NET4以上的基础架构。刚好这个现场在发送命令请求的时候如果请求参数不进行urlencode转码,服务器程序就无法识别相关参数,这下可难倒了我。
通过各方查找资料,网上提供的解决方案要么就是fx4.0+的解决方案,要么就给的
HttpUtility.UrlEncode(str)
调用以后某些字符转码不正常(不支持设置编码方式),无奈之下只能啃.net 源码查找解决方案。功夫不负有心人,终于在高版本的.net源码中找到了相关的踪迹,并将其移植到通用的代码库里面,方便低版本fx愉快的进行UrlEncode转码。为了不引入多余的dll就可以采用下面的方式,可以完美解决低版本.netframework urlencode编码问题,代码可控,可以应对现场各种需求,尤其是这种服务端接口自己不可控的情况下使用。
代码实现
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace Common
{
public static class HttpUti
{
public static string UrlEncode(string value)
{
if (string.IsNullOrEmpty(value))
return value;
int safeCount = 0;
int spaceCount = 0;
for (int i = 0; i < value.Length; i++)
{
char ch = value[i];
if (IsUrlSafeChar(ch))
{
safeCount++;
}
else if (ch == ' ')
{
spaceCount++;
}
}
int unexpandedCount = safeCount + spaceCount;
if (unexpandedCount == value.Length)
{
if (spaceCount != 0)
{
// Only spaces to encode
return value.Replace(' ', '+');
}
// Nothing to expand
return value;
}
int byteCount = Encoding.UTF8.GetByteCount(value);
int unsafeByteCount = byteCount - unexpandedCount;
int byteIndex = unsafeByteCount * 2;
// Instead of allocating one array of length `byteCount` to store
// the UTF-8 encoded bytes, and then a second array of length
// `3 * byteCount - 2 * unexpandedCount`
// to store the URL-encoded UTF-8 bytes, we allocate a single array of
// the latter and encode the data in place, saving the first allocation.
// We store the UTF-8 bytes to the end of this array, and then URL encode to the
// beginning of the array.
byte[] newBytes = new byte[byteCount + byteIndex];
Encoding.UTF8.GetBytes(value, 0, value.Length, newBytes, byteIndex);
GetEncodedBytes(newBytes, byteIndex, byteCount, newBytes);
return Encoding.UTF8.GetString(newBytes);
}
private static bool IsUrlSafeChar(char ch)
{
// Set of safe chars, from RFC 1738.4 minus '+'
/*
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9')
return true;
switch (ch)
{
case '-':
case '_':
case '.':
case '!':
case '*':
case '(':
case ')':
return true;
}
return false;
*/
// Optimized version of the above:
int code = (int)ch;
const int safeSpecialCharMask = 0x03FF0000 | // 0..9
1 << ((int)'!' - 0x20) | // 0x21
1 << ((int)'(' - 0x20) | // 0x28
1 << ((int)')' - 0x20) | // 0x29
1 << ((int)'*' - 0x20) | // 0x2A
1 << ((int)'-' - 0x20) | // 0x2D
1 << ((int)'.' - 0x20); // 0x2E
return IsAsciiLetter(ch) ||
((uint)(code - 0x20) <= (uint)('9' - 0x20) && ((1 << (code - 0x20)) & safeSpecialCharMask) != 0) ||
(code == (int)'_');
}
#region UrlEncode implementation
private static void GetEncodedBytes(byte[] originalBytes, int offset, int count, byte[] expandedBytes)
{
int pos = 0;
int end = offset + count;
Debug.Assert(offset < end && end <= originalBytes.Length);
for (int i = offset; i < end; i++)
{
#if DEBUG
// Make sure we never overwrite any bytes if originalBytes and
// expandedBytes refer to the same array
if (originalBytes == expandedBytes)
{
Debug.Assert(i >= pos);
}
#endif
byte b = originalBytes[i];
char ch = (char)b;
if (IsUrlSafeChar(ch))
{
expandedBytes[pos++] = b;
}
else if (ch == ' ')
{
expandedBytes[pos++] = (byte)'+';
}
else
{
expandedBytes[pos++] = (byte)'%';
expandedBytes[pos++] = (byte)HexConverter.ToCharUpper(b >> 4);
expandedBytes[pos++] = (byte)HexConverter.ToCharUpper(b);
}
}
}
#endregion
public static bool IsAsciiLetter(char c)
{
return (uint)((c | 0x20) - 'a') <= 'z' - 'a';
}
}
class HexConverter
{
public static char ToCharUpper(int value)
{
value &= 0xF;
value += '0';
if (value > '9')
{
value += ('A' - ('9' + 1));
}
return (char)value;
}
}
}
调用方实现如下文章来源:https://www.toymoban.com/news/detail-515888.html
HttpUti.UrlEncode("aaaaa")
完整代码如下文章来源地址https://www.toymoban.com/news/detail-515888.html
public static string HttpPost(string Url, Dictionary<string,string> postData)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
request.Timeout = 3000;
StringBuilder buffer = new StringBuilder();//这是要提交的数据
int i = 0;
//通过泛型集合转成要提交的参数和数据
foreach (string key in postData.Keys)
{
var value = postData[key];
buffer.Append(key+"=" + HttpUti.UrlEncode(value + "") + "&");
}
//通过泛型转化得到的提交数据:LinkTel=第二次测试&CarBrand=1111&Loads=101&UserId=50&SortId=1
//其实与上面的直接拼接参数无异,
byte[] bs = Encoding.Default.GetBytes(buffer.ToString().Trim('&'));//GBK
string responseData = String.Empty;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = bs.Length;
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(bs, 0, bs.Length);
reqStream.Close();
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream myResponseStream = response.GetResponseStream();
StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.Default);
string retString = myStreamReader.ReadToEnd();
myStreamReader.Close();
myResponseStream.Close();
return retString;
}
到了这里,关于C# 手动实现UrlEncode(查看微软底层代码整理)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!