Click here to Skip to main content
15,881,898 members
Articles / Web Development / ASP.NET
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
4.47/5 (8 votes)
29 Aug 2014CPOL2 min read 103.8K   38   14
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

C#
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:

C#
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

C#
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

C#
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

C#
[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:

C#
[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)


Written By
Founder Across Time & Space
United States United States
I am in the process of morphing from a software developer into a portrayer of Mark Twain. My monologue (or one-man play, entitled "The Adventures of Mark Twain: As Told By Himself" and set in 1896) features Twain giving an overview of his life up till then. The performance includes the relating of interesting experiences and humorous anecdotes from Twain's boyhood and youth, his time as a riverboat pilot, his wild and woolly adventures in the Territory of Nevada and California, and experiences as a writer and world traveler, including recollections of meetings with many of the famous and powerful of the 19th century - royalty, business magnates, fellow authors, as well as intimate glimpses into his home life (his parents, siblings, wife, and children).

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, including a two-volume social and cultural history of the U.S. which covers important events from 1620-2006: http://www.lulu.com/spotlight/blackbirdcraven

Comments and Discussions

 
GeneralMy vote of 4 Pin
Vindhyachal_Kumar28-Aug-14 0:29
professionalVindhyachal_Kumar28-Aug-14 0:29 
SuggestionCleaner way Pin
codefabricator27-Aug-14 8:29
codefabricator27-Aug-14 8:29 
GeneralRe: Cleaner way Pin
B. Clay Shannon27-Aug-14 8:49
professionalB. Clay Shannon27-Aug-14 8:49 
GeneralMy vote of 2 Pin
Pawel Cioch26-Aug-14 5:41
Pawel Cioch26-Aug-14 5:41 
SuggestionNon optimal approach - sorry :) Pin
Pawel Cioch26-Aug-14 5:39
Pawel Cioch26-Aug-14 5:39 
AnswerRe: Bad approach - sorry :) Pin
B. Clay Shannon26-Aug-14 5:43
professionalB. Clay Shannon26-Aug-14 5:43 
GeneralRe: Bad approach - sorry :) Pin
Pawel Cioch26-Aug-14 6:50
Pawel Cioch26-Aug-14 6:50 
GeneralRe: Bad approach - sorry :) Pin
B. Clay Shannon26-Aug-14 7:00
professionalB. Clay Shannon26-Aug-14 7:00 
GeneralRe: non optimal approach Pin
Pawel Cioch26-Aug-14 7:04
Pawel Cioch26-Aug-14 7:04 
GeneralRe: non optimal approach Pin
B. Clay Shannon26-Aug-14 7:07
professionalB. Clay Shannon26-Aug-14 7:07 
GeneralRe: Bad approach - sorry :) Pin
Charles J Cooper27-Aug-14 9:49
Charles J Cooper27-Aug-14 9:49 
GeneralRe: Bad approach - sorry :) Pin
B. Clay Shannon27-Aug-14 10:28
professionalB. Clay Shannon27-Aug-14 10:28 
GeneralRe: Bad approach - sorry :) Pin
Sacha Barber28-Aug-14 11:59
Sacha Barber28-Aug-14 11:59 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun25-Aug-14 19:12
Humayun Kabir Mamun25-Aug-14 19:12 

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

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