C# - HttpWebRequest - POST

3
我正在尝试向Apache Web服务器进行HTTP POST。我发现设置ContentLength似乎是请求工作所必需的。我更愿意直接从GetRequestStream()创建一个XmlWriter并将SendChunked设置为true,但这样做会导致请求无限期地挂起。以下是我的请求创建方式:
    private HttpWebRequest MakeRequest(string url, string method)
    {
        HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
        request.Method = method;
        request.Timeout = Timeout; //Property in my class, assume it's 10000
        request.ContentType = "text/xml"; //I am only writing xml with XmlWriter
        if (method != WebRequestMethods.Http.Get)
        {
            request.SendChunked = true;
        }
        return request;
    }

我该如何让SendChunked工作,以便无需设置ContentLength?我不认为需要在将XmlWriter的字符串发送到服务器之前将其存储在某个地方。

编辑:这是导致问题的代码:

    using (Stream stream = webRequest.GetRequestStream())
    {
        using (XmlWriter writer = XmlWriter.Create(stream, XmlTags.Settings))
        {
            Generator.WriteXml<TRequest>(request, writer);
        }
    }

之前我并没有对从GetRequestStream()返回的Stream对象进行使用,我以为在XmlWriter被处理时会自动关闭流,但事实并非如此。

以下其中一个答案让我明白了这一点。我将把它们标记为答案。

就HttpWebRequest而言,我的原始代码完全可以正常工作。


它是否适用于另一个Web服务器(而不是Apache)?您是通过XmlWriter还是RequestStream关闭流? - Mikael Svenson
我没有Windows服务器可以尝试,而且我需要花时间编写一些C#代码来读取这篇文章。我觉得调整HttpWebRequest的设置应该可以解决这个问题。 - jonathanpeppers
3个回答

3

按照您所写的方式应该可以正常工作。我们能否看到实际上传代码?您是否记得关闭流?


我的 XmlWriter 在 using 块中,它仅仅通过一些循环写入一些元素和属性。它调用 WriteEndDocument 来结束文档。我开始怀疑是服务器的问题而不是 .Net 的问题,但我没有 Windows 服务器可以测试,尽管 Apache 的版本相当新。 - jonathanpeppers
WriteEndDocument并不会真正关闭流。在您的代码中的某个地方,您正在调用GetRequestStream(或BeginGetRequestStream);一旦您完成编写XML,您需要在该Stream实例上调用Close方法。如果您不这样做,它将不知道您何时完成上传,连接将永远保持打开状态。 - Aaronaught
你正在使用 XmlWriter,它将释放 XmlWriter 而不是其他。你需要调用 Dispose(或推荐的方法 Close)来释放 StreamXmlWriter 不会为你执行此操作。 - Aaronaught

1

看一下http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.sendchunked.aspx的例子,他们仍然设置了内容长度。实际上,归根结底,如果你要发送数据,你需要告诉接收方你将要发送多少数据。为什么在发送请求之前你不知道你要发送多少数据呢?

ContentLength:

属性值 类型:System..::.Int64 要发送到Internet资源的数据字节数。默认值为-1,表示未设置该属性且没有要发送的请求数据。

为Aaron编辑(我错了):

HttpWebRequest httpWebRequest = HttpWebRequest.Create("http://test") as HttpWebRequest;
httpWebRequest.SendChunked = true;
MessageBox.Show("|" + httpWebRequest.TransferEncoding + "|");

来自 System.Net.HttpWebRequest.SerializeHeaders():

if (this.HttpWriteMode == HttpWriteMode.Chunked)
{
    this._HttpRequestHeaders.AddInternal("Transfer-Encoding", "chunked");
}
else if (this.ContentLength >= 0L)
{
    this._HttpRequestHeaders.ChangeInternal("Content-Length", this._ContentLength.ToString(NumberFormatInfo.InvariantInfo));
}

那将违反HTTP 1.1规范。虽然没有明确定义,但当您使用“SendChunked = true”时,绝对不必设置“ContentLength”属性。一个设置将替换另一个。 - Aaronaught
如果接收到一条消息,其中包含Transfer-Encoding头字段和Content-Length头字段,则必须忽略后者。因此,根据RFC的规定是允许这种情况的,但并不意味着应用程序支持接收POST请求。最简单的方法是获取长度并设置它,但这样做有什么乐趣呢 :) 探索优化解决方案总是很有趣的。 - Mikael Svenson
SendChunked 设置为 true 会在 HTTP 标头中设置 Transfer-Encoding: chunked。它们是相同的。如果需要为 ContentLength 指定值,则会使 SendChunked 属性失去意义。要么他保持流开放,要么服务器出了问题。 - Aaronaught
1
@Aaron:我的编辑后的帖子有一个示例,展示了这种情况并不是真实的,至少在“TransferEncoding”属性中不是。将“SendChunked”=“true”设置为仍然具有空的“TransferEncoding”值。当实际请求发送时是否如此,我无法确定。 - Cory Charlton
我觉得我们离题了,我该如何让SendChunked起作用?如果答案是:你不能,那我就继续下一步。 - jonathanpeppers
显示剩余3条评论

0

我更喜欢使用通用方法来处理这种事情。请看下面的XML发送请求。它将序列化您的XML,然后使用适当的ContentType进行发送:

public bool SendXMLRequest<T>(T entity, string url, string method)
{
    HttpWebResponse response = null;
    bool received = false;
    try
    {
        var request = (HttpWebRequest)WebRequest.Create(url);
        var credCache = new CredentialCache();
        var netCred = new NetworkCredential(YOUR_LOGIN_HERE, YOUR_PASSWORD_HERE, YOUR_OPTIONAL_DOMAIN_HERE);
        credCache.Add(new Uri(url), "Basic", netCred);
        request.Credentials = credCache;
        request.Method = method;
        request.ContentType = "application/xml";
        request.SendChunked = "GET" != method.ToUpper();

        using (var writer = new StreamWriter(request.GetRequestStream()))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));

            using (StringWriter textWriter = new Utf8StringWriter())
            {
                serializer.Serialize(textWriter, entity);
                var xml = textWriter.ToString();
                writer.Write(xml);
            }
        }

        response = (HttpWebResponse)request.GetResponse();    
        received = response.StatusCode == HttpStatusCode.OK; //YOu can change the status code to check. May be created, etc...
    }
    catch (Exception ex) { }
    finally
    {
        if(response != null)
            response.Close();
        }

    return received;
}

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