Click here to Skip to main content
Click here to Skip to main content

How to Send an XML File From a Client to a Server Via REST in a Web API C# Solution

, 29 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Code to send an XML file over the wire using REST / Web API

Files Flying Over Wires

It is common to pass arguments inside the URI to REST methods, to wit:

http://localhost:21608/api/boomerangingTelescopicPhraseMaze/gus/gus/42

(where the "/gus/gus/42" part at the end of the URI are arg vals that are unpacked on the server, perhaps to be used as criteria vals in a query statement).

What if you need to send something, though, that is too long to fit within a URI, such as an entire file? You can theoretically use arcane and quasi-magical gyrations such as the "[FromBody]" and "[FromURI]" tags, but I discovered/uncovered a much easier way. The file is embedded within a stream and passed along with the HttpWebRequest and then simply read (and saved to file, in the code I show) in a Controller within the server.

That about says it all, I reckon; here's the code:

Client Code

        private void buttonIn_Click(object sender, EventArgs e)
        {
            String fullFilePath = @"C:\Platypi\Duckbills_JennyLind_CA.XML";
            String uri = @"http://localhost:21608/api/dplat/sendxml";
            SendXMLFile(fullFilePath, uri, 500);
        }

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

            request.KeepAlive = false;
            request.ProtocolVersion = HttpVersion.Version10;
            request.ContentType = "application/xml";
            request.Method = "POST";

            StringBuilder sb = new StringBuilder();
            using (StreamReader sr = new StreamReader(xmlFilepath))
            {
                String line;
                while ((line = sr.ReadLine()) != null)
                {
                    sb.AppendLine(line);
                }
                byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());

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

                request.ContentLength = postBytes.Length;

                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(ex.Message);
                    request.Abort();
                    return string.Empty;
                }
            }
        }

Bonus for Those Stuck in the Past

For older versions of .NET that do not allow the "using" construct, you can do it this way-- add the following code between "requestStream.Close()" and the brace at the end of the try block:

HttpWebResponse response = null;
try
{
    response = (HttpWebResponse)request.GetResponse();
    return response.ToString();
}
finally
{
    IDisposable disposableResponse = response as IDisposable;
    if (disposableResponse != null) disposableResponse.Dispose();
}

Thanks to ctacke (AKA "The Windows CE/Compact Framework Whisperer") for the code above.

Server Code

    public class DuckbillXMLController : ApiController
    {
    . . .

        [Route("api/dplat/sendXML")] 
        public async void SendInventoryXML()
        {
            XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());
            String saveLoc = @"C:\Duckbills\PlatypiOfJennyLindCalifornia.xml";
            doc.Save(saveLoc);
        }
    }

Not So Fast There!

As you can see, the URI and file paths/names are hard-coded; fixing this to be more elegant/flexible is left as an exercise to the hopefully veracious and hopelessly voracious reader.

Client Determines Saved Filename

Okay, I'll show a little of the "exercise left to the reader" code; If you want the Client to specify the file name to be saved on the server, you could do it the following way if you want to use the same name for the file:

Client Code

private void buttonIn_Click(object sender, EventArgs e)
{
    String fullFilePath = @"C:\Platypi\Duckbills_JennyLind_CA.XML";
    string justFileName = Path.GetFileNameWithoutExtension(fullFilePath);
    // In this case, justFileName will now be simply "Duckbills_JennyLind_CA"
    String uri = String.Format(@"http://localhost:21608/api/dplat/sendXML/{0}", justFileName);
    SendXMLFile(fullFilePath, uri, 500);
}

Note: Passing the entire filename (including the extension (.xml)) discombobulates the Controller method and causes it to fail/not be found. That is why Path.GetFileName() is not good enough, and Path.GetFileNameWithoutExtension() had to be used above.

Alternatively, you could generate the file name by which to save the file on the server and pass that.

Server Code

[Route("api/dplat/sendXML/{filename}")] 
public async void SendInventoryXML(String fileName)
{
    XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());
    String saveLoc = String.Format(@"C:\Duckbill\{0}.xml", fileName);
    doc.Save(saveLoc);
}

Server Determines Saved Filename

If you want the Server to generate/determine the file name to use, you can leave the Client code as originally shown, and change the Server code to something like this:

[Route("api/dplat/sendXML")] 
public async void SendInventoryXML()
{
    XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());
    String fileName = "someEnchantedXMLFileName"; // call a method that generates the appropriate name
    String saveLoc = String.Format(@"C:\Duckbill\{0}.xml", fileName);
    doc.Save(saveLoc);
}

Not So Slow There!

If you find that this tip saves you time, recognizing that time is money, please send me 3.14% of the gross or 42% of the net amount thereby saved. If that is problematic, impossible, or out of the question, a homemade gooseberry pie would suffice.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

B. Clay Shannon
Founder "Across Time & Space"
United States United States
Ideaman and Coder at Across Time & Space, creator of the Windows Store App "Photrax", which can be downloaded as a trial (7 days) from http://apps.microsoft.com/windows/en-us/app/photrax/75c18e6c-96bd-4607-ac43-531aab098ab4
 
Peripatetic and picaresque, I have lived in eight states; specifically, besides my native California (where I was born and where I now again reside) in chronological order: New York, Montana, Alaska, Oklahoma, Wisconsin, Idaho, and Missouri.
 
I am also a writer of both fiction (for which I use a nom de plume, "Blackbird Crow Raven", as a nod to my Native American heritage - I am "½ Cowboy, ½ Indian") and nonfiction: http://www.lulu.com/spotlight/blackbirdcraven
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 4 Pinprofessionalvks900928-Aug-14 1:29 
SuggestionCleaner way Pinmembercodefabricator27-Aug-14 9:29 
GeneralRe: Cleaner way PinprofessionalB. Clay Shannon27-Aug-14 9:49 
GeneralMy vote of 2 PinmemberPawel Cioch26-Aug-14 6:41 
SuggestionNon optimal approach - sorry :) [modified] PinmemberPawel Cioch26-Aug-14 6:39 
AnswerRe: Bad approach - sorry :) PinprofessionalB. Clay Shannon26-Aug-14 6:43 
GeneralRe: Bad approach - sorry :) PinmemberPawel Cioch26-Aug-14 7:50 
GeneralRe: Bad approach - sorry :) PinprofessionalB. Clay Shannon26-Aug-14 8:00 
GeneralRe: non optimal approach PinmemberPawel Cioch26-Aug-14 8:04 
GeneralRe: non optimal approach PinprofessionalB. Clay Shannon26-Aug-14 8:07 
GeneralRe: Bad approach - sorry :) PinmemberCharles J Cooper27-Aug-14 10:49 
GeneralRe: Bad approach - sorry :) PinprofessionalB. Clay Shannon27-Aug-14 11:28 
GeneralRe: Bad approach - sorry :) PinmvpSacha Barber28-Aug-14 12:59 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun25-Aug-14 20:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 29 Aug 2014
Article Copyright 2014 by B. Clay Shannon
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid