【问题标题】:Why is the HttpWebRequest body val null after "crossing the Rubicon"?为什么“越过Rubicon”后HttpWebRequest body val为null?
【发布时间】:2014-03-12 16:43:01
【问题描述】:

我正在尝试将 XML 文件的内容从手持设备(Compact Framework/Windows CE)发送到我的服务器应用程序中的 Web API 方法,如下所示(客户端代码):

public static string SendXMLFile(string xmlFilepath, string uri, int timeout)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;

    request.Method = "POST";

    StringBuilder sb = new StringBuilder();
    using (StreamReader sr = new StreamReader(xmlFilepath))
    {
        String line;
        while ((line = sr.ReadLine()) != null)
        {
            // test to see if it's finding any lines
            //MessageBox.Show(line); <= works fine
            sb.AppendLine(line);
        }
        byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());

        if (timeout < 0)
        {
            request.ReadWriteTimeout = timeout;
            request.Timeout = timeout;
        }

        request.ContentLength = postBytes.Length;
        request.KeepAlive = false;

        request.ContentType = "application/xml";

        try
        {
            Stream requestStream = request.GetRequestStream();

            requestStream.Write(postBytes, 0, postBytes.Length);
            requestStream.Close();

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                return response.ToString();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("SendXMLFile exception " + ex.Message);
            request.Abort();
            return string.Empty;
        }
    }
}

正如您在注释掉的代码中看到的(“

但是,当调用相应的服务器代码时:

[Route("api/DeliveryItems/PostArgsAndXMLFileAsStr")]
public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
    string beginningInvoiceNum = string.Empty;
    string endingInvoiceNum = string.Empty;

    XDocument doc = XDocument.Parse(stringifiedXML);

...“serialNum”和“siteNum”参数符合预期(包含有效的预期值),但正文 (stringifiedXML) 为空。为什么?

更新

我也在客户端中添加了这个:

request.ContentLength = postBytes.Length;
// Did the sb get into the byte array?
MessageBox.Show(request.ContentLength.ToString());

...字节数组确实有数据,因为它显示“112”(XML 文件非常小)。

更新 2

现在我添加了另一个调试消息:

try
{
    Stream requestStream = request.GetRequestStream();
    // now test this:
    MessageBox.Show(string.Format("requestStream length is {0}", requestStream.Length.ToString()));
    requestStream.Write(postBytes, 0, postBytes.Length);
    requestStream.Close();

    using (var response = (HttpWebResponse)request.GetResponse())
    {
        return response.ToString();
    }
}
catch (Exception ex)
{
    MessageBox.Show("SendXMLFile exception " + ex.Message);
    request.Abort();
    return string.Empty;
}

...我什至没有看到“requestStream length is”消息;相反,我看到“SendXMLFileException NotSupportedException”...???

更新 3

我猜这是山楂效应或类似的一个例子。一旦我注释掉了那个调试 (MessageBox.Show()) 语句,我就会回到服务器应用程序中,但是 [FromBody] val null。

然后客户端收到消息,“无法从传输连接中读取数据

更新 4

stringifiedXML 在这里仍然为空:

public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
    string beginningInvoiceNum = string.Empty;
    string endingInvoiceNum = string.Empty;

    XDocument doc = XDocument.Parse(stringifiedXML);

...即使我在回复this question 之后修改了客户端中的代码,如下所示:

public static string SendXMLFile(string xmlFilepath, string uri)
{
    MessageBox.Show(string.Format("In SendXMLFile() - xmlFilepath == {0}, uri == {1}", xmlFilepath, uri));
    string strData = GetDataFromXMLFile();
    HttpWebRequest request = CreateRequest(uri, HttpMethods.POST, strData, "application/xml");

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;

    try
    {
        using (var response = (HttpWebResponse)request.GetResponse())
        {
            return response.GetResponseStream().ToString();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("SendXMLFile exception " + ex.Message);
        request.Abort();
        return string.Empty;
    }
}

private static string GetDataFromXMLFile()
{
    // test data - if it works, get the (same, for now) data from the file
    return @"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>";  //had to remove "s from version num
}

// Based on code from Andy Wiggly (the owner of Wiggly Field in Chicago and the Wiggly chewing gum company?)
public static HttpWebRequest CreateRequest(string uri, HttpMethods method, string data, string contentType)
{
    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
    request.ContentType = contentType;
    ((HttpWebRequest)request).Accept = contentType;
    if (method != HttpMethods.GET && method != HttpMethods.DELETE)
    {
        Encoding encoding = Encoding.UTF8;
        request.ContentLength = encoding.GetByteCount(data);
        request.ContentType = contentType;
        request.GetRequestStream().Write(
          encoding.GetBytes(data), 0, (int)request.ContentLength);
        request.GetRequestStream().Close();
    }
    else
    {
        // If we're doing a GET or DELETE don't bother with this 
        request.ContentLength = 0;
    }
    // Finally, return the newly created request to the caller. 
    return request as HttpWebRequest;
}

注意:我不知道这是否只是关闭服务器的误导性副作用,但我随后在客户端/手持应用程序中看到了这个错误消息:

“System.Net.ProtocolVi...” "请求提交后无法执行此操作。"

更新 5

对于那些想要堆栈跟踪的人,&c:

serNum 和 siteNum 是连接到 uri 中的简单值,如下所示:

string uri = string.Format("http://192.168.125.50:28642/api/FileTransfer/GetHHSetupUpdate?serialNum={0}&clientVersion={1}", serNum, clientVer);

我试图像这样获取堆栈跟踪:

catch (Exception ex)
{
    MessageBox.Show(string.Format("Msg = {0}; StackTrace = {1)", ex.Message, ex.StackTrace));
    request.Abort();
    return string.Empty;
}

...但现在我只看到,“提交请求后无法执行此操作。”

更新 6

我将方法签名更改为:

public static HttpWebResponse SendXMLFile(string xmlFilepath, string uri)

...以及对应的代码:

try
{
    using (var response = (HttpWebResponse)request.GetResponse())
    {
        return response;
    }
}
catch (Exception ex)
{
    MessageBox.Show(string.Format("Msg = {0}; StackTrace = {1)", ex.Message, ex.StackTrace));
    request.Abort();
    return null;
}

...但它没有任何区别(而且我没有看到“StackTrave =”消息,所以它一定是失败的 erstwheres)

更新 7

我在里面放了两个调试字符串:

0)

public static HttpWebRequest CreateRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
    //test:
    MessageBox.Show(string.Format("In CreateRequestNoCredentials(); data passed in = {0}", data));

1) 在 SendXMLFile() 中:

//test:
MessageBox.Show(string.Format("After calling CreateRequestNoCredentials(), request contentLen = {0}, headers = {1}, requestUri = {2}", 
    request.ContentLength, request.Headers, request.RequestUri));

...我看到了这个:

...但是在第二个有机会向我展示血腥细节之前,服务器收到 null 正文值,因此崩溃,然后客户端发出相同的旧“此操作不能提交请求后执行”投诉。

更新 8

针对建议,“我怀疑如果您在 CreateRequest 调用后删除 KeepAlive 和 ProtocolVersion 的设置,异常就会消失。”,我将我的代码更改为:

    HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, strData, "application/xml");

    //test:
    MessageBox.Show(string.Format("After calling CreateRequestNoCredentials(), request contentLen = {0}, headers = {1}, requestUri = {2}", 
        request.ContentLength, request.Headers, request.RequestUri));

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;


public static HttpWebRequest CreateRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
    //test:
    MessageBox.Show(string.Format("In CreateRequestNoCredentials(); data passed in = {0}", data));

    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
    request.ContentType = contentType;
    ((HttpWebRequest)request).Accept = contentType;

    if (method != HttpMethods.GET && method != HttpMethods.DELETE)
    {
        Encoding encoding = Encoding.UTF8;
        request.ContentLength = encoding.GetByteCount(data);
        request.ContentType = contentType;
        request.GetRequestStream().Write(
          encoding.GetBytes(data), 0, (int)request.ContentLength);
        request.GetRequestStream().Close();
    }
    else
    {
        // If we're doing a GET or DELETE don't bother with this 
        request.ContentLength = 0;
    }
    // Finally, return the newly created request to the caller. 
    return request as HttpWebRequest;
}

...到这个:

    HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, strData, "application/xml");

    //test:
    MessageBox.Show(string.Format("After calling CreateRequestNoCredentials(), request contentLen = {0}, headers = {1}, requestUri = {2}", 
        request.ContentLength, request.Headers, request.RequestUri));

public static HttpWebRequest CreateRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
    //test:
    MessageBox.Show(string.Format("In CreateRequestNoCredentials(); data passed in = {0}", data));

    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
    request.ContentType = contentType;
    ((HttpWebRequest)request).Accept = contentType;
    // moved from elsewhere to here:
    ((HttpWebRequest)request).KeepAlive = false;
    ((HttpWebRequest)request).ProtocolVersion = HttpVersion.Version10;

    if (method != HttpMethods.GET && method != HttpMethods.DELETE)
    {
        Encoding encoding = Encoding.UTF8;
        request.ContentLength = encoding.GetByteCount(data);
        request.ContentType = contentType;
        request.GetRequestStream().Write(
          encoding.GetBytes(data), 0, (int)request.ContentLength);
        request.GetRequestStream().Close();
    }
    else
    {
        // If we're doing a GET or DELETE don't bother with this 
        request.ContentLength = 0;
    }
    // Finally, return the newly created request to the caller. 
    return request as HttpWebRequest;
}

...但我仍然收到相同的错误消息(“提交请求后无法执行此操作”),并且当它到达服务器时 stringifiedXML 仍然为空。

更新 9

这是我通过 Fiddler 2 发送我理解的内容时得到的结果(如果您没有视觉超能力,请右键单击图像并在新选项卡中打开):

...但我不知道我在看什么...有用吗?它失败了吗? “body == 0”列让我暂停/让我认为它失败了,但“204”似乎意味着“服务器成功处理了请求,但没有返回任何内容”...

更新 10

这是修复 uri 后 Fiddler 的尖叫声,我确实到达了服务器应用程序中的断点,并发送了良好的数据:

更新 11

更改此代码:

string strData = sb.ToString();
HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, strData, "application/xml");

...到这个:

string strData = @sb.ToString(); // GetDataFromXMLFile();
string body = String.Format("\"{0}\"", strData);
HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, body, "application/json"); 

...我现在在 stringifiedXML 中得到这个:“

...所以我现在得到:“System.Xml.XmlException 未被用户代码处理 H结果=-2146232000 消息=文件意外结束。第 1 行,位置 15..."

无论如何,这是一个进步......

更新 12

根据在 Fiddle 中作为“请求正文”传递的字符串的确切构成/格式,结果完全不同。

将此作为请求正文:

<?xml version="1.0"?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>

...stringifiedXML 为空

将此作为请求正文:

"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

...stringifiedXML 完全相同 ("")

...但是有一个例外:

System.Xml.XmlException 未被用户代码处理 H结果=-2146232000 Message='1.0' 是一个意外的标记。预期的标记是 '"' 或 '''。第 1 行,第 15 位。 源=System.Xml 行号=1 线位置=15 源URI="" 堆栈跟踪: 在 System.Xml.XmlTextReaderImpl.Throw(异常 e) 在 System.Xml.XmlTextReaderImpl.Throw(字符串 res,String[] args) 在 System.Xml.XmlTextReaderImpl.ThrowUnexpectedToken(字符串 expectedToken1,字符串 expectedToken2) 在 System.Xml.XmlTextReaderImpl.ParseXmlDeclaration(布尔 isTextDecl) 在 System.Xml.XmlTextReaderImpl.Read() 在 System.Xml.Linq.XDocument.Load(XmlReader 阅读器,LoadOptions 选项) 在 System.Xml.Linq.XDocument.Parse(字符串文本,LoadOptions 选项) 在 System.Xml.Linq.XDocument.Parse(字符串文本) 在 C:\HandheldServer\HandheldServer 中的 HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() \Controllers\DeliveryItemsController.cs:第 63 行 内部异常:

将此作为请求正文:

"<?xml version="1.0"?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

...stringifiedXML 是 "

倒数第二个,以此作为请求正文:

"<?xml version=\"1.0\"?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

...stringifiedXML 是完全一样的东西 ("")

...但我得到了这个例外:

System.InvalidOperationException 未被用户代码处理 H结果=-2146233079 Message=Sequence 不包含任何元素 源=System.Core 堆栈跟踪: 在 System.Linq.Enumerable.First[TSource](IEnumerable`1 源) 在 C:\HandheldServer\HandheldServer\Controllers\DeliveryItemsController.cs:line 109 中的 HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() 内部异常:

最后,如果我通过这个,在 angulars 中使用(尽管是虚假的)vals:

"<?xml version=\"1.0\"?><LocateAndLaunch><Tasks>Some Task</Tasks><Locations>Some Location</Locations></LocateAndLaunch>"

...我仍然得到“序列不包含任何元素”

这个方法比Rachel Canning还挑剔!它想要什么——啤酒里有鸡蛋?!?

更新 13

使用此代码:

public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
    XDocument doc = XDocument.Parse(await Request.Content.ReadAsStringAsync()); 

...或者这个:

。 . .XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());

...这是传入的 stringifiedXML:

"一些任务一些位置"

...我得到了例外: "System.Xml.XmlException 未被用户代码处理 H结果=-2146232000 Message=Root 元素丢失。”

使用此代码(相同的 stringifiedXML):

XDocument doc = XDocument.Parse(stringifiedXML);

... 我明白了,“System.InvalidOperationException 未被用户代码处理 H结果=-2146233079 Message=Sequence 不包含任何元素 源=System.Core 堆栈跟踪: 在 System.Linq.Enumerable.First[TSource](IEnumerable`1 源) 在 C:\HandheldServer\HandheldServer 中的 HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() \Controllers\DeliveryItemsController.cs:第 109 行 内部异常:“

IOW,根据我解析传入字符串的方式,我得到“缺少根元素”或“序列不包含任何元素”

什么是 Deuce McAlistair MacLean Virginia Weeper?!? “&lt;LocateAndLaunch>”不是根元素吗?不是“某些任务”和“某些位置”元素吗?

【问题讨论】:

  • 堆栈跟踪是什么?
  • 这里没有足够的信息。首先,您说“serialNum”和“siteNum”参数符合预期。我不知道那些是从哪里来的。他们是否以某种方式来自请求?此外,返回 response.ToString() 不会给你任何有用的东西。也不会返回response.GetResponseStream().ToString()。如果要返回文本,则必须从响应流中创建一个StreamReader,并读取数据。查看StreamReader.ReadToEnd 方法,该方法将读取并返回字符串中的全部内容。
  • 显示协议冲突异常的完整堆栈跟踪,并告诉我们它发生在哪一行。更好的是,单步执行代码即可确定问题。
  • 您可以考虑获取 Fiddler 并查看从客户端发出的数据。如果 POST 数据在请求中(我怀疑是这样),那么问题出在服务器上。当然,然后您必须将更改还原给客户端,或者弄清楚您是如何破坏它的。
  • 可能会抛出异常,因为您在写入请求流后正在设置请求对象的属性。我怀疑如果在 CreateRequest 调用之后删除KeepAliveProtocolVersion 的设置,异常就会消失。

标签: encoding asp.net-web-api httpwebrequest fiddler frombodyattribute


【解决方案1】:

对于这样的动作方法

public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML,
                                              string serialNum, string siteNum)
{}

请求消息必须是这样的。我在这里使用 JSON。

POST http://localhost:port/api/values/PostArgsAndXMLFileAsStr?serialNum=1&siteNum=2 HTTP/1.1
Content-Type: application/json
Host: localhost:port
Content-Length: 94

"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

请求正文需要包含双引号,顺便说一句。这样,绑定应该可以正常工作。

所以,使用内容类型application/json 发布消息并像这样格式化正文。

string content = @"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>";
string body = String.Format("\"{0}\"", content);

在更改客户端代码中的任何内容之前,请使用 Fiddler 发送与上述类似的 POST,以确保它在 Web API 端有效。之后,更改您的客户端以确保它输出的请求只是使用 Fiddler 的工作请求。

【讨论】:

  • xml数据为什么要是application/json?
  • 我应该通过 Fiddler 发送什么来测试您第二个代码块中的所有内容(包括引号中的正文)吗?
  • 请参阅更新 9 了解我的 Fiddler faddle。
  • 在 Visual Studio 中运行 web api 项目,在 action 方法中放置一个断点,然后从 Fiddler 发送请求。如果stringifiedXML 参数具有 XML 值,则当它中断时,它可以正常工作。关于你的问题,Why should it be application/json when it's xml data?,一个简单的类型是从整个身体中绑定的。所以,我们如何指定身体变得很奇怪。我们指定 JSON 并发送一个值,在这种情况下恰好是一个 XML 字符串。您可以将 XML 作为内容类型发送,如果您希望将 XML 绑定到复杂类型,则可以发送 XML。
  • 对不起,你必须弄清楚。您在ValuesController 中有断点吗? Fiddler 中的 URI 具有 /values 但手持设备可能会碰到其他一些控制器 DeliveryItems 可能是 - 我不知道。我所知道的是您的提琴手请求正在访问控制器和操作方法。否则你不会得到204。肯定不会失败。
猜你喜欢
  • 2013-06-11
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
  • 1970-01-01
  • 2012-11-04
  • 2021-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多