How can I avoid a "Bad Request - Invalid Hostname" error when making a REST call from a Compact Framework client?
Asked Answered
L

2

8

I used this code from here to try to call a REST Controller method on a Web API server app from a Compact Framework client:

public static void SendXMLFile3(string uri, string data)
{
    WebRequest request = WebRequest.Create (uri);
    request.Method = "POST";
    string postData = data;
    byte[] byteArray = Encoding.UTF8.GetBytes (postData);
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = byteArray.Length;
    Stream dataStream = request.GetRequestStream ();
    dataStream.Write (byteArray, 0, byteArray.Length);
    dataStream.Close ();
    WebResponse response = request.GetResponse ();
    MessageBox.Show(((HttpWebResponse) response).StatusDescription);
    dataStream = response.GetResponseStream ();
    StreamReader reader = new StreamReader (dataStream);
    string responseFromServer = reader.ReadToEnd();
    MessageBox.Show(responseFromServer);
    reader.Close ();
    dataStream.Close ();
    response.Close ();
}

...I had earlier tried this code, which I got from the book "Microsoft .NET Compact Framework":

public static void SendXMLFile2(string uri, string data)
{
    WebRequest req = WebRequest.Create(uri);
    req.Method = "Post";
    req.ContentType = "text/plain; charset=utf-8";
    byte[] encodedBytes = Encoding.UTF8.GetBytes(data);
    req.ContentLength = encodedBytes.Length;

    Stream requestStream = req.GetRequestStream();
    requestStream.Write(encodedBytes, 0, encodedBytes.Length);
    requestStream.Close();

    WebResponse result = req.GetResponse();
    MessageBox.Show(result.ToString());
}

...but I get "400 - Bad Request" with the new (as well as the old) code.

My initial attempt also does not work, with the same result (400):

public static string SendXMLFile(string xmlFilepath, string uri, int timeout)
{
    HttpWebRequest myHttpWebRequest=(HttpWebRequest)WebRequest.Create(uri);
    myHttpWebRequest.AllowWriteStreamBuffering=false;
    string postData = "<Command><DSD><line_id>1</line_id><invoice_no>david_dsd</invoice_no>. . .</DSD></Command>"; // TODO: if this works, replace it with the real data
    myHttpWebRequest.Method="POST";
    UTF8Encoding encodedData = new UTF8Encoding();
    byte[]  byteArray=encodedData.GetBytes(postData);
    myHttpWebRequest.ContentType = "application/xml";
    myHttpWebRequest.ContentLength=byteArray.Length;
    Stream newStream=myHttpWebRequest.GetRequestStream();
    newStream.Write(byteArray,0,byteArray.Length);
    newStream.Close();
    HttpWebResponse myHttpWebResponse=(HttpWebResponse)myHttpWebRequest.GetResponse();
    return myHttpWebResponse.StatusDescription;
}

There is much more about the plethora of variations I have tried here, where I have reached my length-of-post limit.

UPDATE

Note that the Server code doesn't know/care that the file is XML:

[HttpPost]
[Route("api/inventory/sendXML/{userId}/{pwd}/{filename}")]
public async Task SendInventoryXML(String userId, String pwd, String fileName)
{
    Task task = Request.Content.ReadAsStreamAsync().ContinueWith(t =>
    {
        var stream = t.Result;
        using (FileStream fileStream = File.Create(String.Format(@"C:\HDP\{0}.xml", fileName), (int)stream.Length))
        {
            byte[] bytesInStream = new byte[stream.Length];
            stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
            fileStream.Write(bytesInStream, 0, bytesInStream.Length);
        }
    });
}

UPDATE 2

I tried Charles to see if it would pick up the local HTTP traffic, but it is also deaf to such (like Fiddler, without special ministrations, anyway). This is what Charles looks like after getting the "400 - Bad Request" error:

enter image description here

UPDATE 3

I found this suggestion somewhere to get Fiddler to show local HTTP traffic:

Tools--> Fiddler Options. Choose Connections tab. Check the 'USe PAC Script' option.

...but it didn't work - I still see no HTTP traffic when getting the "400 (Bad Request)" message.

UPDATE 4

I am now seeing "400 (Bad Request)" in Fiddler 2, too; to get it, I enter any of the following in Postman (don't see this in Fiddler when calling from CE/CF/handheld app):

http://SHANNON2:21608/api/inventory/sendXML/su/su/blablee // Hostname
http://SHANNON2.:21608/api/inventory/sendXML/su/su/blablee // Hostname with Fiddler-fooler appended "."
http://192.168.125.50:21608/api/inventory/sendXML/su/su/blablee // IP Address

(Fiddler does not capture anything if I replace the hostname or IP Address with "localhost")

Note: For these URLs in Postman, I have "Post" (as opposed to GET, etc.) selected, and an XML file attached.

Inspectors.Headers in Fiddler shows:

POST /api/inventory/sendXML/su/su/blablee HTTP/1.1

Although I consider this a minor debugging victory, I still don't see why I'm getting the "400" error.

Fiddler tells me, in the Inspectors.WebView pane:

Bad Request - Invalid Hostname

--------------------------------------------------------------------------------

HTTP Error 400. The request hostname is invalid.

How can that be? When I run it from Postman, I hit the breakpoint in the server - if the hostname is invalid, why is it being reached?

UPDATE 5

Testing the call from Fiddler Composer and Postman, the only way I can reach the breakpoint in the server code is by using "localhost" - replacing that with the PC's IPAddress (192.168.125.50) or HostName (SHANNON2) does not reach the breakpoint. While "interesting," calling "localhost" from the handheld device is obviously not an option.

UPDATE 6

Related new question here.

UPDATE 7

The crux of the biscuit was adding at the command prompt either this:

netsh http add urlacl url=http://shannon2:80/ user=everyone

...or this:

netsh http add urlacl url=http://shannon2:8080/ user=everyone

See Update 5 here for more details

Lightish answered 8/9, 2014 at 17:59 Comment(9)
For application/x-www-form-urlencoded you may need to run the text data through Uri.EscapeDataString to apply percent encodingDav
Most API's that accept xml need a properly formed xml document (i.e with an <?xml .. element)Dav
@AlexK.: Thanks, but if you mean "data = Uri.EscapeDataString(data);" EscapeDataString() does not seem to be available in this version (1.1) of .NETLightish
if you just send something simple like "hello world" across the wire, does it work? You need to determine if it's a transport problem, or something like encoding, etc.Ursulina
It works from a modern Winforms client, and it works from Fiddler (using the "Compose" tab and uploading a file), it just won't work from my Compact Framework client. Of course, my code has to differ somewhat from the code in the modern Winforms client, as not everything available today was available back in the days of the Diplodocus, when .NET (version 1.1) first came out.Lightish
If your using fiddle have you compared a working composer request with a failing WebRequest capture? What are the differences?Dav
I have not seen any HTTP traffic in Fiddler yet; I just found out how to set it up for local traffic, and will czech it out today.Lightish
The device routes differently that any app running on your local machine. That would explain why an app works and the device doesn't. That's the reason I've been suggesting using only an IP address, never a machine name. Does it work calling it from a WinForms app on a separate machine?Ursulina
@ctacke: I have been using the IP Address.Lightish
L
0

The crux of the biscuit was adding at the command prompt either this:

netsh http add urlacl url=http://shannon2:80/ user=everyone

...or this:

netsh http add urlacl url=http://shannon2:8080/ user=everyone

See Update 5 here for more details

Lightish answered 9/9, 2015 at 20:9 Comment(0)
C
26

Adding my observations, if you are facing the issue Bad Request - Invalid Hostname while calling the API from Postman. Something like this.

enter image description here

Consider adding the Host in Headers, it will work.

enter image description here

Updated after someone is not able to view the image: If the images are not loading, then basically this you have to do:

Basically, you have to add Content-Length and Host. Postman auto calculates when the request is sent. These can be enabled in postman by unhiding auto-generated headers in Postman. or you can set by your own.

Cringle answered 9/3, 2021 at 6:15 Comment(5)
I sure wish the images, which contain the entire content of this answer, were not broken links. The gist of it is: enable the Host:<calculated when request is sent> header in the Headers tab of the request.Deming
the images are not broken. how you are accessing? in any specific browser, or device?. I am not sure what problem are you facing? @user1944491Cringle
you're right - my VPN denies access to the site they're hosted on and exiting the VPN allows the images to show. Nevertheless, I think a textual description of the solution -- since it works! -- will improve the longevity of this answer's value. @CringleDeming
@user1944491 I have added in the answer, what was shown in the image. Hope that would be helpful.Cringle
I think it improves the answer. But I ONLY had to add the generated host. Content-Length is typically immaterial in a GET request.Deming
L
0

The crux of the biscuit was adding at the command prompt either this:

netsh http add urlacl url=http://shannon2:80/ user=everyone

...or this:

netsh http add urlacl url=http://shannon2:8080/ user=everyone

See Update 5 here for more details

Lightish answered 9/9, 2015 at 20:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.