Sending WebDAV Requests in .NET

Earlier today one of my coworkers, John Bocharov, asked me if I had ever done any WebDAV coding in .NET - specifically sending PUT and DELETE requests. I replied that I had, but it had been several months ago, and each time that I had written any WebDAV-related code samples it was for a specific purpose and not very exhaustive. Just the same, I promised John that if I found any of my old code samples I would send them to him. After a bit of searching through my archives I was able to find enough code snippets to throw together a quick sample for PUT and DELETE that John could use, but it made me start thinking about putting together a more complete sample by adding a few extra WebDAV methods, thereby creating a better example to keep around.

With that in mind, the code sample in this blog post shows how to send several of the most-common WebDAV requests using C# and common .NET libraries. There's a bit of intentional redundancies in each section of the sample - I did this because I was trying to make each section somewhat self-sufficient so you can copy and paste a little easier. I present the WebDAV methods the in the following order:

WebDAV Method Notes
PUT This section of the sample writes a string as a text file to the destination server as "foobar1.txt". Sending a raw string is only one way of writing data to the server, in a more common scenario you would probably open a file using a steam object and write it to the destination. One thing to note in this section of the sample is the addition of the "Overwrite" header, which specifies that the destination file can be overwritten.
COPY This section of the sample copies the file from "foobar1.txt" to "foobar2.txt", and uses the "Overwrite" header to specify that the destination file can be overwritten. One thing to note in this section of the sample is the addition of the "Destination" header, which obviously specifies the destination URL. The value for this header can be a relative path or an FQDN, but it may not be an FQDN to a different server.
MOVE This section of the sample moves the file from "foobar2.txt" to "foobar1.txt", thereby replacing the original uploaded file. As with the previous two sections of the sample, this section of the sample uses the "Overwrite" and "Destination" headers.
GET This section of the sample sends a WebDAV-specific form of the HTTP GET method to retrieve the source code for the destination URL. This is accomplished by sending the "Translate: F" header and value, which instructs IIS to send the source code instead of the processed URL. In this specific sample I am only using text files, but if the requests were for ASP.NET or PHP files you would need to specify the "Translate: F" header/value pair in order to retrieve the source code.
DELETE This section of the sample deletes the original file, thereby cleaning off all of the sample files from the destination server.
MKCOL This section of the sample creates a folder named "foobar3" on the destination server; as far as WebDAV on IIS is concerned, the MKCOL method is a lot like the old DOS MKDIR command.
DELETE This section of the sample deletes the folder from the destination server.

That wraps up the section descriptions, and with that taken care of - here is the code sample:

using System;
using System.Net;
using System.IO;
using System.Text;

class WebDavTest
{
   static void Main(string[] args)
   {
      try
      {
         // Define the URLs.
         string szURL1 = @"http://localhost/foobar1.txt";
         string szURL2 = @"http://localhost/foobar2.txt";
         string szURL3 = @"http://localhost/foobar3";

         // Some sample text to put in text file.
         string szContent = String.Format(
            @"Date/Time: {0} {1}",
            DateTime.Now.ToShortDateString(),
            DateTime.Now.ToLongTimeString());

         // Define username and password strings.
         string szUsername = @"username";
         string szPassword = @"password";

         // --------------- PUT REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpPutRequest =
            (HttpWebRequest)WebRequest.Create(szURL1);

         // Set up new credentials.
         httpPutRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);

         // Pre-authenticate the request.
         httpPutRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpPutRequest.Method = @"PUT";

         // Specify that overwriting the destination is allowed.
         httpPutRequest.Headers.Add(@"Overwrite", @"T");

         // Specify the content length.
         httpPutRequest.ContentLength = szContent.Length;

         // Optional, but allows for larger files.
         httpPutRequest.SendChunked = true;

         // Retrieve the request stream.
         Stream requestStream =
            httpPutRequest.GetRequestStream();

         // Write the string to the destination as a text file.
         requestStream.Write(
            Encoding.UTF8.GetBytes((string)szContent),
            0, szContent.Length);

         // Close the request stream.
         requestStream.Close();

         // Retrieve the response.
         HttpWebResponse httpPutResponse =
            (HttpWebResponse)httpPutRequest.GetResponse();

         // Write the response status to the console.
         Console.WriteLine(@"PUT Response: {0}",
            httpPutResponse.StatusDescription);

         // --------------- COPY REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpCopyRequest =
            (HttpWebRequest)WebRequest.Create(szURL1);

         // Set up new credentials.
         httpCopyRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);

         // Pre-authenticate the request.
         httpCopyRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpCopyRequest.Method = @"COPY";

         // Specify the destination URL.
         httpCopyRequest.Headers.Add(@"Destination", szURL2);

         // Specify that overwriting the destination is allowed.
         httpCopyRequest.Headers.Add(@"Overwrite", @"T");

         // Retrieve the response.
         HttpWebResponse httpCopyResponse =
            (HttpWebResponse)httpCopyRequest.GetResponse();

         // Write the response status to the console.
         Console.WriteLine(@"COPY Response: {0}",
            httpCopyResponse.StatusDescription);

         // --------------- MOVE REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpMoveRequest =
            (HttpWebRequest)WebRequest.Create(szURL2);

         // Set up new credentials.
         httpMoveRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);

         // Pre-authenticate the request.
         httpMoveRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpMoveRequest.Method = @"MOVE";

         // Specify the destination URL.
         httpMoveRequest.Headers.Add(@"Destination", szURL1);

         // Specify that overwriting the destination is allowed.
         httpMoveRequest.Headers.Add(@"Overwrite", @"T");

         // Retrieve the response.
         HttpWebResponse httpMoveResponse =
            (HttpWebResponse)httpMoveRequest.GetResponse();

         // Write the response status to the console.
         Console.WriteLine(@"MOVE Response: {0}",
            httpMoveResponse.StatusDescription);

         // --------------- GET REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpGetRequest =
            (HttpWebRequest)WebRequest.Create(szURL1);

         // Set up new credentials.
         httpGetRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);

         // Pre-authenticate the request.
         httpGetRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpGetRequest.Method = @"GET";

         // Specify the request for source code.
         httpGetRequest.Headers.Add(@"Translate", "F");

         // Retrieve the response.
         HttpWebResponse httpGetResponse =
            (HttpWebResponse)httpGetRequest.GetResponse();

         // Retrieve the response stream.
         Stream responseStream =
            httpGetResponse.GetResponseStream();

         // Retrieve the response length.
         long responseLength =
            httpGetResponse.ContentLength;

         // Create a stream reader for the response.
         StreamReader streamReader =
            new StreamReader(responseStream, Encoding.UTF8);

         // Write the response status to the console.
         Console.WriteLine(
            @"GET Response: {0}",
            httpGetResponse.StatusDescription);
         Console.WriteLine(
            @"  Response Length: {0}",
            responseLength);
         Console.WriteLine(
            @"  Response Text: {0}",
            streamReader.ReadToEnd());

         // Close the response streams.
         streamReader.Close();
         responseStream.Close();

         // --------------- DELETE REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpDeleteFileRequest =
            (HttpWebRequest)WebRequest.Create(szURL1);

         // Set up new credentials.
         httpDeleteFileRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);

         // Pre-authenticate the request.
         httpDeleteFileRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpDeleteFileRequest.Method = @"DELETE";

         // Retrieve the response.
         HttpWebResponse httpDeleteFileResponse =
            (HttpWebResponse)httpDeleteFileRequest.GetResponse();

         // Write the response status to the console.
         Console.WriteLine(@"DELETE Response: {0}",
            httpDeleteFileResponse.StatusDescription);

         // --------------- MKCOL REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpMkColRequest =
            (HttpWebRequest)WebRequest.Create(szURL3);

         // Set up new credentials.
         httpMkColRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);

         // Pre-authenticate the request.
         httpMkColRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpMkColRequest.Method = @"MKCOL";

         // Retrieve the response.
         HttpWebResponse httpMkColResponse =
            (HttpWebResponse)httpMkColRequest.GetResponse();

         // Write the response status to the console.
         Console.WriteLine(@"MKCOL Response: {0}",
            httpMkColResponse.StatusDescription);

         // --------------- DELETE REQUEST --------------- //

         // Create an HTTP request for the URL.
         HttpWebRequest httpDeleteFolderRequest =
            (HttpWebRequest)WebRequest.Create(szURL3);

         // Set up new credentials.
         httpDeleteFolderRequest.Credentials =
            new NetworkCredential(szUsername, szPassword);
         
         // Pre-authenticate the request.
         httpDeleteFolderRequest.PreAuthenticate = true;

         // Define the HTTP method.
         httpDeleteFolderRequest.Method = @"DELETE";

         // Retrieve the response.
         HttpWebResponse httpDeleteFolderResponse =
            (HttpWebResponse)httpDeleteFolderRequest.GetResponse();

         // Write the response status to the console.
         Console.WriteLine(@"DELETE Response: {0}",
            httpDeleteFolderResponse.StatusDescription);

      }
      catch (Exception ex)
      {
         Console.WriteLine(ex.Message);
      }
   }
}

When you run the code sample, if there are no errors you should see something like the following output:

PUT Response: Created
COPY Response: Created
MOVE Response: No Content
GET Response: OK
  Response Length: 30
  Response Text: Date/Time: 2/9/2010 7:21:46 PM
DELETE Response: OK
MKCOL Response: Created
DELETE Response: OK
Press any key to continue . . .

Since the code sample cleans up after itself, you should not see any files or folders on the destination server when it has completed executing. To see the files and folders actually created and deleted on the destination server, you can step through the code in a debugger.

Closing Notes

I should point out that I did not include examples of the WebDAV PROPPATCH/PROPFIND and LOCK/UNLOCK methods in this sample because they require processing the XML responses, and that was way outside the scope of what I had originally wanted to do with this sample. I might write a follow-up blog later that shows how to use those methods, but I'm not making any promises. ;-]

I hope this helps.

1 Comment

  • I started receiving 200 OK for the result of PUT, COPY, and MOVE instead of 201, 201, and 204.
    What would be causing this?

    Clark

Comments have been disabled for this content.