HttpWebRequest长URI解决方案?

11

我在使用 HttpWebRequest 时遇到了一个问题,如果 URI 超过2048个字符,请求将失败并返回404错误,尽管服务器完全能够处理那么长的URI。我知道这一点是因为,当使用 HttpWebRequest 提交相同的 URI 时会导致错误,但是如果直接将该 URI 粘贴到浏览器地址栏中,则可以正常工作。

我目前的解决方法是允许用户设置兼容性标志,以便在 URI 太长的情况下将参数作为 POST 请求发送,但这并不理想,因为我正在使用 RESTful 协议,应该使用 GET 进行查询。而且并不能保证协议的其他实现者会接受 POST 的查询。

是否有 .NET 中另一个类具有与 HttpWebRequest 等效的功能,不会受到 URI 长度限制,我可以使用?
我知道 WebClient 也可以用,但我真的不想使用它,因为我需要完全控制 HTTP Headers,而 WebClient 限制了这种能力。

编辑

因为 Shoban 要求它:

http://localhost/BBCDemo/sparql/?query=PREFIX+rdf%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E%0D%0APREFIX+rdfs%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23%3E%0D%0APREFIX+xsd%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%23%3E%0D%0APREFIX+skos%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2004%2F02%2Fskos%2Fcore%23%3E%0D%0APREFIX+dc%3A+%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%0D%0APREFIX+po%3A+%3Chttp%3A%2F%2Fpurl.org%2Fontology%2Fpo%2F%3E%0D%0APREFIX+timeline%3A+%3Chttp%3A%2F%2Fpurl.org%2FNET%2Fc4dm%2Ftimeline.owl%23%3E%0D%0ASELECT+*+WHERE+{%0D%0A++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+dc%3Atitle+%3Ftitle+.%0D%0A++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Ashort_synopsis+%3Fsynopsis-short+.%0D%0A++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Amedium_synopsis+%3Fsynopsis-med+.%0D%0A++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Along_synopsis+%3Fsynopsis-long+.%0D%0A++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Amasterbrand+%3Fchannel+.%0D%0A++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Agenre+%3Fgenre+.%0D%0A++++%3Fchannel+dc%3Atitle+%3Fchanneltitle+.%0D%0A++++OPTIONAL+{%0D%0A++++++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Abrand+%3Fbrand+.%0D%0A++++++++%3Fbrand+dc%3Atitle+%3Fbrandtitle+.%0D%0A++++}%0D%0A++++OPTIONAL+{%0D%0A++++++++%3Chttp%3A%2F%2Fwww.bbc.co.uk%2Fprogrammes%2Fb00n4d6y%23programme%3E+po%3Aversion+%3Fver+.%0D%0A++++++++%3Fver+po%3Atime+%3Finterval+.%0D%0A++++++++%3Finterval+timeline%3Astart+%3Fstart+.%0D%0A++++++++%3Finterval+timeline%3Aend+%3Fend+.%0D%0A++++}%0D%0A}&default-graph-uri=&timeout=30000

以下哪个被编码到查询字符串中:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX po: <http://purl.org/ontology/po/>
PREFIX timeline: <http://purl.org/NET/c4dm/timeline.owl#>
SELECT * WHERE {
  <http://www.bbc.co.uk/programmes/b00n4d6y#programme> dc:title ?title .
  <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:short_synopsis ?synopsis-short .
  <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:medium_synopsis ?synopsis-med .
  <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:long_synopsis ?synopsis-long .
  <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:masterbrand ?channel .
  <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:genre ?genre .
  ?channel dc:title ?channeltitle .
  OPTIONAL {
    <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:brand ?brand .
    ?brand dc:title ?brandtitle .
  }
  OPTIONAL {
    <http://www.bbc.co.uk/programmes/b00n4d6y#programme> po:version ?ver .
    ?ver po:time ?interval .
    ?interval timeline:start ?start .
    ?interval timeline:end ?end .
  }

}

(该内容为单个封闭的 HTML 段落标记,无需翻译,直接保留即可)

我想查看超过2048个字符的URL;-) - Shoban
@Shoban 为您添加了一个示例。 - RobV
4个回答

6
我使用的协议是RESTful,查询应该使用GET。但是没有理由不能使用POST进行查询;对于非常长的请求数据,您必须使用POST,因为非常长的URI并未得到全球支持,也从未得到过。这是HTTP没有达到REST理想的一个领域。
通常不在普通HTML级别上使用POST的原因是为了防止浏览器提示重新加载,并促进例如书签等功能。但是对于HttpWebRequest,您没有这些问题,所以可以放心使用POST。Web应用程序应该使用参数或URI路径部分来区分写请求和查询,而不仅仅是请求方法。(当然,从GET方法发出的写请求仍应被拒绝。)

是的,我查看了协议的文档,它说实现者可能支持POST查询,因此他们应该支持它们,但不能保证。 - RobV
从方法论和哲学观点来看,这是无意义的,但它却有效!谢谢。 - Pavel Shkleinik

3
我认为HttpWebRequest实际上并不与您所说的GET URL大小不兼容。我有两个理由:
  1. 在我的工作中,我使用HttpWebRequest发送HTTP GET请求,长度超过2048个字符而没有问题。我不确定最长的是多少,但我们谈论的是10,000多个字符。(这主要是在Web应用程序和运行在Tomcat下的Solr实例之间进行的。)

  2. .NET确实对GET URL长度有一些限制,但我知道的那些限制远高于2048个字符。例如,我今天从我的分析器中了解到WebRequest.Create(string url)调用Uri类构造函数, 它的文档记录了如果“uriString的长度超过65534个字符”,则会抛出一个UriFormatException异常。

我不确定您的问题可能出在哪里,如果不是 HttpWebRequest 本身。您知道您的 Web 服务在什么情况下会返回 HTTP 404(即“未找到”)吗?(我假设 404 来自您的 Web 服务,而不是在 .NET 的深处伪造。)我还想再次检查您复制到浏览器中的地址是否与 .NET 发送的地址实际上相同;正如 feroze 所建议的那样,您应该使用网络嗅探工具进行此操作。如果两个地址相同,则可以比较 .NET 案例和浏览器案例之间的 HTTP 标头差异。 (顺便说一句,我个人发现 Fiddler 在这些任务的 HTTP 调试方面比 wireshark 更方便一些。)
另请参阅此相关问题:HttpWebRequest 与将 URL 粘贴到地址栏有何不同(功能)?

2
以下是涵盖越来越长的url值的代码段,直到发生异常: HttpWebRequest实例:
using System.Net;

...

StringBuilder url = new StringBuilder("http://example.com?p=");
try
{
    for (int i = 1; i < Int32.MaxValue; i++)
    {
        url.Append("0");
        HttpWebRequest request = HttpWebRequest.CreateHttp(url.ToString());
    }
}
catch (Exception ex)
{
    Console.Out.WriteLine("Error occurred at url length: " + url.Length);
    Console.Out.WriteLine(ex.GetType().ToString() + ": " + ex.Message);
    return;
}
Console.Out.WriteLine("Completed without error!");

在我的电脑上(在运行 .NET 4.5 的 LINQPad 中),这个代码片段将会输出:
Error occurred at url length: 65520
System.UriFormatException: Invalid URI: The Uri string is too long.

0

根据RFC3986,您的查询字符串有误。URI中不允许使用'{'和'}'字符。


呃,给出的URI是由.Net的Uri.EscapeDataString()方法生成的,不确定符合哪个URI RFC。 - RobV

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