Why is the HttpWebRequest body val null after “crossing the Rubicon”?

余生颓废 提交于 2019-12-25 02:49:14

问题


I am trying to send the contents of an XML file from a handheld device (Compact Framework/Windows CE) to a Web API method in my server app like so (Client code):

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;
        }
    }
}

As you can see in the commented out code ("<= works fine"), I've tested it and the data I want is being added to the StringBuilder. There is no exception being thrown (I don't see "SendXMLFile exception ").

However, when the corresponding server code is called:

[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);

...the "serialNum" and "siteNum" args are as expected (contain the valid expected values) but the body (stringifiedXML) is null. Why?

UPDATE

I added this, too, in the client:

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

...and the byte array does have the data, as it shows me "112" (the XML file is quite small).

UPDATE 2

Now I added yet another debug msg:

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;
}

...and I don't even see the "requestStream length is" message; instead I see, "SendXMLFileException NotSupportedException"...???

UPDATE 3

I guess this is an example of the Hawthorn Effect or similar. Once I commented out that debug (MessageBox.Show()) statement, I'm back to making it into the server app, but with the [FromBody] val null.

Then the client has the message, "Unable to read data from the transport connection"

UPDATE 4

stringifiedXML is still null here:

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

    XDocument doc = XDocument.Parse(stringifiedXML);

...even after I modified the code in the client following a response to this question like so:

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;
}

Note: I don't know if this is just a misleading side-effect of shutting down the server, but I subsequently saw this err msg in the client/handheld app:

"System.Net.ProtocolVi..." "This operation cannot be performed after the request has been submitted."

UPDATE 5

For those wanting a Stack Trace, &c:

serNum and siteNum are simple values that get concatenated into the uri like so:

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

I tried to get the Stack Trace like so:

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

...but now I'm only seeing, "This operation cannot be performed after the request has been submitted."

UPDATE 6

I changed the method signature to this:

public static HttpWebResponse SendXMLFile(string xmlFilepath, string uri)

...and the corresponding code to this:

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;
}

...but it made no difference (and I see no "StackTrave = " message, so it must be failing erstwheres)

UPDATE 7

I put two debug strings in:

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) In SendXMLFile():

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

...and I see this:

...but then before the second one gets a chance to show me the gory details, the server receives the null body value, crashes thuswith, and then the client whin[g]es with the same old "This operation cannot be performed after the request has been submitted" complaint.

UPDATE 8

In response to the suggestion, "I suspect that if you remove the setting of KeepAlive and ProtocolVersion after the CreateRequest call, the exception will go away.", I changed my code from this:

    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;
}

...to this:

    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;
}

...and yet I still get that same err msg ("This operation cannot be performed after the request has been submitted") and stringifiedXML is still null when it hits the server.

UPDATE 9

Here is what I get when I send what I understand to be what I should via Fiddler 2 (right-click the image and open in a new tab if you don't have visual superpowers):

...but I don't know what I'm looking at...did it work? Did it fail? The "body == 0" column gives me pause/makes me think it failed, yet "204" seems to mean "The server successfully processed the request, but is not returning any content"...

UPDATE 10

Here is the Fiddler scream shot after fixing the uri, and I do reach the breakpoint in the server app, with good data sent:

UPDATE 11

With changing this code:

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

...to this:

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

...I'm now getting this in stringifiedXML: "

...and so I'm now getting: "System.Xml.XmlException was unhandled by user code HResult=-2146232000 Message=Unexpected end of file has occurred. Line 1, position 15..."

It's an improvement, anyway...

UPDATE 12

Depending on the exact makeup/formatting of the string passed as "Request Body" in Fiddle, the results differ radically.

With this as Request Body:

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

...stringifiedXML is null

With this as Request Body:

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

...stringifiedXML is exactly the same ("")

...but there is an exception:

System.Xml.XmlException was unhandled by user code HResult=-2146232000 Message='1.0' is an unexpected token. The expected token is '"' or '''. Line 1, position 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) at System.Xml.XmlTextReaderImpl.ThrowUnexpectedToken(String expectedToken1, String expectedToken2) at System.Xml.XmlTextReaderImpl.ParseXmlDeclaration(Boolean isTextDecl) at System.Xml.XmlTextReaderImpl.Read() at System.Xml.Linq.XDocument.Load(XmlReader reader, LoadOptions options) at System.Xml.Linq.XDocument.Parse(String text, LoadOptions options) at System.Xml.Linq.XDocument.Parse(String text) at HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() in c:\HandheldServer\HandheldServer \Controllers\DeliveryItemsController.cs:line 63 InnerException:

With this as Request Body:

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

...stringifiedXML is "

Penultimately, with this as Request Body:

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

...stringifiedXML is exactly the same thing ("")

...but I get this exception:

System.InvalidOperationException was unhandled by user code HResult=-2146233079 Message=Sequence contains no elements Source=System.Core StackTrace: at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) at HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() in c:\HandheldServer\HandheldServer\Controllers\DeliveryItemsController.cs:line 109 InnerException:

And finally, if I pass this, with (albeit bogus) vals within the angulars:

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

...I STILL get "sequence contains no elements"

This method is more picky than Rachel Canning! What does it want - egg in its beer?!?

UPDATE 13

With this code:

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

...or this:

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

...and this as the incoming stringifiedXML:

"Some TaskSome Location"

...I get the exception: "System.Xml.XmlException was unhandled by user code HResult=-2146232000 Message=Root element is missing."

With this code (same stringifiedXML):

XDocument doc = XDocument.Parse(stringifiedXML);

... I get, "System.InvalidOperationException was unhandled by user code HResult=-2146233079 Message=Sequence contains no elements Source=System.Core StackTrace: at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) at HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() in c:\HandheldServer\HandheldServer \Controllers\DeliveryItemsController.cs:line 109 InnerException: "

IOW, depending on how I parse the incoming string, I get either "Root element is missing" or "Sequence contains no elements"

What the Deuce McAlistair MacLean Virginia Weeper?!? Isn't "<LocateAndLaunch>" a root element? Aren't "Some Task" and "Some Location" elements?


回答1:


For the action method like this

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

the request message must be like this. I use JSON here.

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>"

The request body needs to contain the double quotes, BTW. With this, binding should work correctly.

So, post the message with content type application/json and format the body like this.

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

Before, you change anything in the client side code, use Fiddler to send a POST like the one above to ensure it works in the web API side. After that, change your client side to make sure it outputs the request just the working request with Fiddler.



来源:https://stackoverflow.com/questions/22358231/why-is-the-httpwebrequest-body-val-null-after-crossing-the-rubicon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!