0

I want to upload a .json file to a server as a file. I am building a UWP app using C#. I make the POST request using Windows.Web.Http.HttpClient. The post request returns 200, but the file didn't upload. The URL links to a folder on the company's web hosting platform. If I check the page or the bluehost cpanel page, there are no recent file uploads to correspond with the app's supposedly successful request. How do I upload a file to the server? Do I need to provide login credentials? Does Microsoft UWP even support FTP anymore? It really only wants me to use HttpClient.

I've tried various methods, but this one (which I mostly translated from Microsoft's example C++ code for uploading a simple file) is the first time it hasn't returned a 405 or just dumped the contents of the url. I have tried using the HttpWebRequest format and the System.Net.Http prefix.

fileOpenPicker.FileTypeFilter.Add(".json");
IStorageFile jsonFile = await fileOpenPicker.PickSingleFileAsync();
IRandomAccessStream stream = await jsonFile.OpenAsync(FileAccessMode.Read);
Windows.Web.Http.HttpStreamContent jsonContent = new HttpStreamContent(stream);

jsonContent.Headers.Append("Content-Type", "application/json");

Windows.Web.Http.Headers.HttpContentDispositionHeaderValue disposition = new Windows.Web.Http.Headers.HttpContentDispositionHeaderValue("form-data");
jsonContent.Headers.ContentDisposition = disposition;

disposition.Name ="fileForUpload";
disposition.FileName = "testSurveyFile.json";

Windows.Web.Http.HttpMultipartFormDataContent postContent = new HttpMultipartFormDataContent();
postContent.Add(jsonContent); // Add the binary data content as a part of the form data content.

Windows.Web.Http.HttpResponseMessage httpResponseMessage;
string httpResponseBody;

try
{

    // Send the POST request.
    Uri requestUri = new Uri(uploadurl);
    Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
    httpResponseMessage = await httpClient.PostAsync(requestUri, postContent);
    httpResponseMessage.EnsureSuccessStatusCode();
    Debug.WriteLine(httpResponseMessage);
    httpResponseBody = httpResponseMessage.Content.ReadAsStringAsync().GetResults();
}
catch (Exception ex)
{
    Debug.WriteLine(ex);
}

This is what httpResponseMessage returns:

StatusCode: 200, 
ReasonPhrase: '', 
Version: 3, 
Content: Windows.Web.Http.HttpStreamContent, 
Headers:
{
  server: nginx/1.14.1
  cache-control: no-store, no-cache, must-revalidate
  set-cookie: PHPSESSID=4f21dec8695cea2b75d009c47d9b94bb; path=/
  date: Fri, 02 Aug 2019 15:28:34 GMT
  pragma: no-cache
}{
  content-type: text/html; charset=UTF-8
  expires: Thu, 19 Nov 1981 08:52:00 GMT
}

but there is no new file at the location.

To clarify, the files are hosted on bluehost. If I can't POST to bluehost regularly (like I can with this site: http://ptsv2.com/t/gizn2-1564768263/d/373971039#) then how do I get around that? It is an https:// prefix, does the s mean I need credentials or certifications for ssl? Where or how could I find that out?

This is the output of httpResponseBody, the testSurveyFile.json file is an old one this upload is meant to replace or overwrite. This is the correct index. I hope this information clears some things up.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
 <head>
  <title>Index of /npjTest/SurveyStuff</title>
 </head>
 <body>
<h1>Index of /npjTest/SurveyStuff</h1>
  <table>
   <tr><th valign="top">&nbsp;</th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
   <tr><th colspan="5"><hr></th></tr>
<tr><td valign="top">&nbsp;</td><td><a href="/npjTest/">Parent Directory</a>       </td><td>&nbsp;</td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="TestSurvey0/">TestSurvey0/</a>           </td><td align="right">2019-07-26 11:51  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="blankHTMLPage.html">blankHTMLPage.html</a>     </td><td align="right">2019-07-22 10:25  </td><td align="right">4.7K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="composer.json">composer.json</a>          </td><td align="right">2019-07-15 08:00  </td><td align="right"> 60 </td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="composer.lock">composer.lock</a>          </td><td align="right">2019-07-15 08:00  </td><td align="right">2.2K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="error_log">error_log</a>              </td><td align="right">2019-07-31 09:19  </td><td align="right">9.5K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="surveyConfig.php">surveyConfig.php</a>       </td><td align="right">2019-07-30 10:12  </td><td align="right">675 </td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="surveyFinalPage.php">surveyFinalPage.php</a>    </td><td align="right">2019-07-31 14:35  </td><td align="right"> 12K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="surveyLogic.php">surveyLogic.php</a>        </td><td align="right">2019-07-31 14:34  </td><td align="right">9.8K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="testSurveyFile.json">testSurveyFile.json</a>    </td><td align="right">2019-07-31 14:37  </td><td align="right">4.0K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="testSurveyPage.html">testSurveyPage.html</a>    </td><td align="right">2019-07-22 12:18  </td><td align="right">4.7K</td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="vendor/">vendor/</a>                </td><td align="right">2019-07-15 07:59  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top">&nbsp;</td><td><a href="vendorZIP2.zip">vendorZIP2.zip</a>         </td><td align="right">2019-07-15 07:58  </td><td align="right">2.6M</td><td>&nbsp;</td></tr>
   <tr><th colspan="5"><hr></th></tr>
</table>
<address>Apache Server at ___________.com Port 443</address>
</body></html>

It seems like I should be doing an HTTP PUT request instead of a POST. I had considered it and tried running PutAsync instead, but I'm getting errors related to redirects. Specifically, if I turn AllowAutoRedirect to false (to bypass an error stating that 'The HTTP redirect request must be confirmed by the user') it comes back with a 301 response code. I'm not sure why it's redirecting. When I send the uri, should that uri go to the index (/SurveyStuff) or the file I want it to overwrite (/SurveyStuff/testSurveyFile.json)?

If I use the uri ending in /SurveyStuff/testSurveyFile.json for a PUT request I get a 405 (Not Allowed) response. I realize this what I should have been doing from the beginning, though I did try it before coming here. This response is returned from both Postman and my app. Is it bluehost that's blocking the request for security purposes? I've tried various FTP and site credentials, but they haven't made a difference.

Noah
  • 1
  • 4
  • `https://` just means it uses the HTTPS protocol, which, yes, means it requires SSL. That shouldn't really matter to your code though. It would be interesting to find out what the value of `httpResponseBody` is, if you change that line to `httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();` The headers indicate the content-type is "text/html", which indicates you may be getting something interesting in response. – Heretic Monkey Aug 02 '19 at 20:38
  • Please [edit] your question to include the contents... – Heretic Monkey Aug 02 '19 at 20:50
  • I'm sorry, I thought I did add the contents of the output to my question. Is there anything else you need or could be useful? – Noah Aug 05 '19 at 12:32
  • Looks like an issue with your server-side code. Do you have code there that handles uploads/ I know nothing about bluehost; maybe you have to delete the old file before uploading a new one/ – Heretic Monkey Aug 05 '19 at 12:37
  • I deleted the old file, but the new one does not replace it when I try to upload via my script. I don't believe I've written any code hosted on the server to upload the file sent by the POST request. I figured the code in this function would do the upload. I'll admit, I'm also very new to bluehost and web programming in general. Perhaps there is some setting that's preventing me from posting from the app to cpanel. I'll look into bluehost some more. I really appreciate the help. – Noah Aug 05 '19 at 15:57
  • I chatted with support from bluehost, and they said that as long as microsoft UWP doesn't require a microsoft server, bluehost should support post requests from the app. I'm fairly certain that UWP doesn't require a microsoft server as I've already used it to pull information from a database hosted on bluehost. I could try running a GET request and seeing what that gets me. – Noah Aug 05 '19 at 16:38

2 Answers2

1

Disclaimer: I must admit I know very little about Windows.Web.Http.HttpClient. Here are some potential fixes, and if those don't work, some other solutions.

405 Issue:

  • If you were receiving a 405 error, I would double check the url that you are sending it to and make sure it's correct, and can accept POST requests.
  • I would also check to see if you require a VPN connection to use that url or not (try to hit it from a web browser).

Credential Issue: Although this would be odd, because usually you would receive a 401 (unauthorized) error code, you might want to include credentials just to be safe.

For System.Net.Http.HttpClient Credentials:

var httpClientHandler = new HttpClientHandler()
{
    Credentials = new NetworkCredential(username, password, domainName),
};

var httpClient = new HttpClient(httpClientHandler);

Solution #1: System.Net.Http.HttpClient multipart-form data (this).

IInputStream inputStream = await file.OpenAsync(FileAccessMode.Read);

HttpMultipartFormDataContent multipartContent = new HttpMultipartFormDataContent();

multipartContent.Add(new HttpStreamContent(inputStream),"myFile",file.Name);

HttpClient client = new HttpClient();
HttpResponseMessage response = await client.PostAsync(uri,multipartContent);

Solution #2 System.Net.Http.HttpClient multipart-form data (this).

You'll have to tweak this a bit as he is uploading an image as a byte array, and I would suggest using more Using statements as they automatically dispose the object.

You could convert your file to a byte array if you wish:

byte[] bytes = System.IO.File.ReadAllBytes(filename);

Solution #3 If those do not work, you can try some alternative methods:

I hope this at least puts you on the right track :).

MedievalCoder
  • 627
  • 7
  • 10
  • Thank you for such a detailed post. I have tried it with credentials, and with a POST method, it would dump the contents of the target url into the httpResponseMessage debug, and the file still wouldn't get uploaded. I also am still fairly inexperience with http and web programming in general. So forgive me for asking, how would I check if the URL accepts POST requests or not? I tried the code on this site [link](http://ptsv2.com/t/gizn2-1564768263/d/373971039#) that's for testing POST requests and the json file showed up there with all the correct contents. What does this mean? – Noah Aug 02 '19 at 18:02
  • Correct me if I'm wrong, but your httpResponseMessage is containing the correct return info, it's just that the json data isn't being posted to the site? If you control the web api that is trying to post the contents of your data it should have something like [HttpPost] attribute above the method. If you are unable to control it, I suggest using Postman to try and send some test json data to the file server you are using just to see what happens. If you can send your data to a different url, then the problem lies most likely with the file server you're using. Try testing with Postman. – MedievalCoder Aug 05 '19 at 19:58
  • I'm trying to use HTTP POST to upload a file. Is this the correct use of this method? I'm worried that I was mistaken in trying to use that because a testing .php file that accepts post requests and echoes a quick debug message does successfully echo that message into httpResponseMessage/Body. I could write something that accepts the .json data and uses that to update other files, but I'd also like to use the original method to upload images as well. So I'd still need to make uploading files work. In the meantime, I will give Postman a try for testing. Thank you. – Noah Aug 05 '19 at 20:34
  • I used postman to send the POST request and a PUT request with the .json file (and a .txt file just to check), but it did not seem to upload to the server. What does this tell me? I got the impression from the customer support that bluehost should support POST requests, but there doesn't seem to be anything wrong on my end. – Noah Aug 06 '19 at 17:17
  • If you are performing the correct actions on Postman to Post and it still isn't showing up on your server, then the issue is the server not your code. This is further validated by the fact that your code could upload a file to a different server, just not yours. Just to be sure you did it correctly on Postman, however, check out this [link](https://stackoverflow.com/questions/16015548/tool-for-sending-multipart-form-data-request). Also it is standard that Post is used to create a new item and Put is used to update an item and is usually sent with an Id. – MedievalCoder Aug 06 '19 at 19:40
0

I spoke with bluehost support for a second time, and this time they said bluehost does not support HTTP POST requests from any external source, including Postman. It seems I'll need to find another place to host this file. I apologize for my confusion on this point. I appreciate the help, as it improved my understanding of HTTP requests and web programming in general. Thank you very much.

Noah
  • 1
  • 4