C# HttpWebResponse 头编码

9

我遇到了如下问题。我访问一个地址,我知道该地址使用301重定向。

使用HttpWebRequest loHttp = (HttpWebRequest)WebRequest.Create(lcUrl);loHttp.AllowAutoRedirect = false;以便不被重定向。

现在,我获取响应的头以识别新的URL。

使用loWebResponse.GetResponseHeader("Location");

问题是,由于此URL包含希腊字符,返回的字符串是混乱的(由于编码问题)。

完整的代码如下:

HttpWebRequest loHttp = (HttpWebRequest)WebRequest.Create(lcUrl);
loHttp.ContentType = "application/x-www-form-urlencoded";
loHttp.Method = "GET";

Timeout = 10000;

loHttp.AllowAutoRedirect = false;
HttpWebResponse loWebResponse = (HttpWebResponse)loHttp.GetResponse();

string url= loWebResponse.Headers["Location"];

1
默认情况下,HttpWebRequest 会遵循重定向,因此如果服务器发送 301/302 状态码,则会发出新请求以使用 Location 标头获取资源。因此,一旦获取了最终资源,响应中将不再有 Location 标头,所以我想知道为什么 loWebResponse.GetResponseHeader("Location") 返回的不是空字符串。另外,您是否使用 FireBug 验证了该站点对 Location 标头执行正确的编码? - Darin Dimitrov
1
我没有明确说明设置了“loHttp.AllowAutoRedirect = false;”,以便我可以检查重定向的URL。 - Alexandros B
3个回答

6
如果您使用默认行为(loHttp.AllowAutoRedirect = true),而代码不起作用(您未被重定向到新资源),这意味着服务器未正确编码Location标头。在浏览器中是否可以重定向?
例如,如果重定向URL是http://site/Μία_Σελίδα,则Location标头必须类似于http://site/%CE%95%CE%BD%CE%B9%CE%B1%CE%AF%CE%BF_%CE%94%CE%B5%CE%
更新:
经过进一步调查,我开始怀疑HttpWebRequest存在某些奇怪的问题。当发送请求时,服务器会发送以下响应:
HTTP/1.1 301 Moved Permanently
Date: Fri, 11 Dec 2009 17:01:04 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Location: http://www.site.com/buy/κινητή-σταθερή-τηλεφωνία/c/cn69569/
Content-Length: 112
Content-Type: text/html; Charset=UTF-8
Cache-control: private
Connection: close
Set-Cookie: BIGipServerpool_webserver_gr=1007732746.36895.0000; path=/


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

正如我们所看到的,Location头包含希腊字符,这些字符没有进行url编码。我不确定这是否符合HTTP规范。我们可以确定的是,Web浏览器可以正确解释它。
有趣的部分来了。似乎HttpWebRequest没有使用UTF-8编码来解析响应头,因为当分析Location头时,它会给出:http://www.site.com/buy/κινηÏή-ÏÏαθεÏή-ÏηλεÏÏνία/c/cn69569/,这显然是错误的。当它尝试重定向到此位置时,服务器会响应一个新的重定向,直到达到最大重定向次数并抛出异常。
我找不到任何指定HttpWebRequest在解析响应头时使用的编码的方法。如果我们手动使用TcpCLient,则可以完美地运行:
using (var client = new TcpClient())
{
    client.Connect("www.site.com", 80);

    using (var stream = client.GetStream())
    {
        var writer = new StreamWriter(stream);
        writer.WriteLine("GET /default/defaultcatg.asp?catg=69569 HTTP/1.1");
        writer.WriteLine("Host: www.site.com");
        writer.WriteLine("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.2) Gecko/20090805 Shiretoko/3.5.2");
        writer.WriteLine("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        writer.WriteLine("Accept-Language: en-us,en;q=0.5");
        writer.WriteLine("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7");
        writer.WriteLine("Connection: close");
        writer.WriteLine(string.Empty);
        writer.WriteLine(string.Empty);
        writer.WriteLine(string.Empty);
        writer.Flush();

        var reader = new StreamReader(stream);
        var response = reader.ReadToEnd();
        // When looking at the response it correctly reads 
        // Location: http://www.site.com/buy/κινητή-σταθερή-τηλεφωνία/c/cn69569/
    }
}

我对这种行为感到非常困惑。有没有办法指定HttpWebRequest使用的正确编码?也许应该设置一些请求头?

作为解决方法,您可以尝试修改执行重定向的asp页面并对Location标头进行urlencode。例如,在ASP.NET应用程序中执行Response.Redirect(location)时,位置将自动进行HTML编码,并且任何非标准字符都将转换为相应的实体。

例如,如果您执行以下操作:Response.Redirect("http://www.site.com/buy/κινητή-σταθερή-τηλεφωνία/c/cn69569/");在ASP.NET应用程序中,Location标头将被设置为:

http://www.site.com/buy/%ce%ba%ce%b9%ce%bd%ce%b7%cf%84%ce%ae-%cf%83%cf%84%ce%b1%ce%b8%ce%b5%cf%81%ce%ae-%cf%84%ce%b7%ce%bb%ce%b5%cf%86%cf%89%ce%bd%ce%af%ce%b1/c/cn69569

看起来经典ASP并非如此。


如果我将它设置为true,那么我会得到一个异常(超时或最大重定向异常)。在浏览器中,它可以很好地达到正确的页面。因此我的猜测是我在读取位置头方面做错了些什么。 - Alexandros B
您能否发布实际的URL,以便我可以查看一下?或者它可能不是公开可访问的? - Darin Dimitrov
2
在 .Net 中,标题的解析由封装在 WebHeaderCollection 类中的“纯 ASCII”编码处理。这符合 RFC 2616。分发 Location 标头的人做错了,但大多数浏览器“只是处理它”,假定字符集为 UTF-8(实际八位字节流中的内容)。 - IDisposable

1

我不会预期返回的字符串是格式错误的...你是如何确定它是格式错误的?该字符串应该采用像UTF-8这样的Unicode格式,可以轻松地表示希腊字符串。

可能是因为您没有希腊字体来表示该字符串?


“malformed”指的是不可读的编码。这就是getResponseHeader返回的内容:"http://www.site.com/buy/κινηÏή-ÏÏαθεÏή-ÏηλεÏÏνία/c/cn69569/" - Alexandros B
在 Visual Studio 中,它看起来有点不同:S 但仍然可以看到中间部分被破坏了。 - Alexandros B

1
如Darin Dimitrov所解释的那样,我认为头部编码是由HttpWebResponse类中的一个错误引起的。我们曾经遇到过同样的问题,我们想要将一个包含非ASCII字符的cookie添加到头部(Set-Cookie),在我们特定的情况下,这将是挪威字母'Æ'、'Ø'和'Å'(大写和小写)。我们无法弄清楚如何让HeaderEncoding工作,但我们找到了一种使用Base64编码的cookie的解决方法。请注意,这只适用于您控制客户端和服务器端的情况(或者您可以说服负责服务器端代码的人为您添加Base64编码...)

在服务器端:

var cookieData = "This text contains Norwegian letters; ÆØÅæøå";
var cookieDataAsUtf8Bytes = System.Text.Encoding.UTF8.GetBytes(cookieData);
var cookieDataAsUtf8Base64Encoded = Convert.ToBase64String(cookieDataAsUtf8Bytes);
var cookie = new HttpCookie("MyCookie", cookieDataAsUtf8Base64Encoded);
response.Cookies.Add(cookie);

在客户端:

var cookieDataAsUtf8Bytes = Convert.FromBase64String(cookieDataAsUtf8Base64Encoded);
var cookieData = System.Text.Encoding.UTF8.GetString(cookieDataAsUtf8Bytes);

请注意,客户端上的cookieDataAsUtf8Base64Encoded是cookie的数据部分(即'MyCookie=[data]',其中'MyCookie='已被剥离)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接