0

我正在尝试将 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;
        }
    }
}

正如您在注释掉的代码中看到的(“<= 工作正常”),我已经对其进行了测试,并且我想要的数据正在添加到 StringBuilder。没有抛出异常(我没有看到“SendXMLFile 异常”)。

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

[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 为空。

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

更新 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);

...即使在我修改了客户端中的代码之后,对这个问题的回应如下:

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 未由用户代码 HResult=-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 未被用户代码处理 HResult=-2146232000 Message='1.0' 是一个意外的标记。预期的标记是 '"' 或 '''。第 1 行,位置 15。Source=System.Xml LineNumber=1 LinePosition=15 SourceUri="" StackTrace: at System.Xml.XmlTextReaderImpl.Throw(Exception e) at System。 Xml.XmlTextReaderImpl.Throw(String res, String[] args) 在 System.Xml.XmlTextReaderImpl.ThrowUnexpectedToken(String expectedToken1, String expectedToken2) 在 System.Xml.XmlTextReaderImpl.ParseXmlDeclaration(Boolean isTextDecl) 在 System.Xml.XmlTextReaderImpl.Read( )在 System.Xml.Linq.XDocument.Parse(字符串文本,LoadOptions 选项)在 System.Xml.Linq.XDocument 的 System.Xml.Linq.XDocument.Load(XmlReader 阅读器,LoadOptions 选项)。

以此作为请求正文:

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

...stringifiedXML 是“

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

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

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

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

System.InvalidOperationException 未被用户代码处理 HResult=-2146233079 Message=Sequence 不包含任何元素 Source=System.Core StackTrace: at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) at HandheldServer.Controllers.DeliveryItemsController.d__2 c:\HandheldServer\HandheldServer\Controllers\DeliveryItemsController.cs 中的 .MoveNext():第 109 行 InnerException:

最后,如果我通过这个,在角度内使用(尽管是虚假的)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 未被用户代码 HResult=-2146232000 处理=缺少根元素。”

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

XDocument doc = XDocument.Parse(stringifiedXML);

...我得到,“ System.InvalidOperationException 未由用户代码 HResult=-2146233079 处理消息=序列不包含元素 Source=System.Core StackTrace: at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) at HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() 在 c:\HandheldServer\HandheldServer \Controllers\DeliveryItemsController.cs:line 109 InnerException:"

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

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

4

1 回答 1

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 的工作请求。

于 2014-03-13T02:07:41.107 回答