.NET Server

Creating a Class 1 WebDAV Server

In this article

Creating a Class 1 WebDAV Server

Class 1 WebDAV Server provides basic file management features available in every WebDAV server. With a Class 1 server, you can create files, copy, move and delete files and folders as well as create, read and delete custom properties on each file or folder. Note that many WebDAV clients, such as Microsoft Office applications, Microsoft Web Folders/mini redirector client, Mac OS X WebDAV client and OpenOffice require Class 2 server. Class 1 server does not protect files from concurrent modifications, so many clients treat Class 1 as read-only, making it unusable for real world applications. We will explain how to extend Class 1 server with Class 2 features in this article.

Creating Basic WebDAV Server Using Wizard.

Here we will use the 'ASP.NET WebDAV Server Application' Wizard to create a Class 1 WebDAV server. Go to File -> New Project -> Visual C# -> Web menu and start the 'ASP.NET WebDAV Server Application' wizard. On the Storage Type step, select 'Store File and metadata in file system' and live the default path in File storage location field.

Select Store File and metadata in file system and live the default path in File storage location field

While the wizard provides two options - file system and MS SQL Database, you can program the engine to store data in virtually any place, for example, in CMS/DMS/CRM or in any non-relational database. You can keep parts of your data in different places, for example, tree structure in database, while file content in file system; or you can integrate several data sources to be displayed under a single root, there are countless number of implementations.

On the Standard Features step clear the Class 2 checkbox, leaving only Class 1, that is always grayed, as soon as Class 1 is the minimum functionality supported by any WebDAV server.

Class 1 checkbox is always grayed and other checkboxes are clear

On the Extended Features step clear all checkboxes except 'Create Custom Handler for GET verb'.

clear all checkboxes except Create Custom Handler for GET verb

Leave the anonymous authentication selected in the Authentication step.

Anonymous authentication is selected

Below is the Solution Explorer with your project (you must check the 'Show All Files' button on the Solution Explorer toolbar to see the App_Data folder):

The Solution Explorer with your project

There is only one aspx file in the project that displays 'Your server is running' page with several options when you run the project.

The files published via WebDAV are placed to \App_Data\WebDAV\Storage\ folder. The wizard have populated this folder with sample files.

The Wizard have created the folder to keep WebDAV logs: \App_Data\WebDAV\Logs\. When you run the project the engine creates a log file called WebDAVlog.txt in \App_Data\WebDAV\Logs\. If you experience any issues with your server the first thing to do is to examine this log file. You may find exceptions logged in it that may give you the idea about the issue.

Never create logs in your \bin folder in Web Application or MVC project. As soon as the log file is updated your application will restart.

However, you still can create logs in the \bin folder for HttpListener-based projects.

The path to \App_Data\WebDAV\Storage\ and to \App_Data\WebDAV\Logs\ folders are specified in RepositoryPath and LogPath application settings in web.config. If you decide to change the path to these folders make sure your web application has enough permissions to modify files in the new location.

Core Classes in Your Project

As we mentioned earlier, here we can compile and run the application. However, as soon as most WebDAV clients require Class 2 server we will only examine the code generated by the wizard. Let's create a class diagram and look what classes were generated. Here are the core classes of your project:

The diagram of the core classes of your project

There are 2 types of items in a basic WebDAV repository: folders, represented by IFolder interface and files represented by IFile interface. Both IFolder and IFile interfaces are derived from IHierarchyItem interface. Files and folders are generated by the factory method called GetHierarchyItemAsync that you must override in your class derived from ContextAsync. Below is a simplified GetHierarchyItemAsync implementation provided for demo purposes (the real code generated by the wizard is more complicated and provides more logic for URL decoding):

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The DavContext class holds the execution context that is specific to each WebDAV request. The ContextAsync provides 3 overloaded constructors, two of which are optimized for use in IIS/ASP.NET-based server and in the HttpListener-based server. The third constructor is provided to support any other hosting environment.

You will create a separate context class instance in each request and pass it to the DavEngineAsync.RunAsync. The engine will parse WebDAV XML request and generate the response.

In the case of ASP.NET based WebDAV server, all WebDAV requests are processed by HTTP handler. The wizard has created the DavHandler class for this purpose, that implements IHttpHandler (again, for demo purposes, this code is simplified competing to what wizard generates):

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

The DavEngineAsync.RunAsync method is thread safe. While the above example creates a separate engine instance for each request, you are free to create a single engine instance, save it in application state and reuse in each request. However, note that you must create a separate instance of your DAV context class in each request.

In your web.config file, you will find 2 entries for DavHandler class. The first one is used if your server runs in IIS Classic mode and the second one is used in IIS Integrated mode:

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

Below we will summarize how your application works. Here is the typical workflow:

1. Create an instance DavEngineAsync class.
2. Set the license string using DavEngineAsync.License property.
3. In each HTTP request:
a. Create an instance of your DAV context class derived from ContextAsync.
b. Pass your DAV context class to DavEngineAsync.RunAsync method.
c. Engine calls ContextAsync.GetHierarchyItemAsync.
d. Engine calls members of interfaces IFile, IFolder, etc.

IT Hit WebDAV Storage Interfaces

Now let's see how the storage is represented in IT Hit WebDAV Server library. The storage is the place where you keep files tree structure, files content, custom properties, etc.

The IFile interface, in addition to IHierarchyItem, implements IContent interface that provides methods for managing file content. The class diagram below shows these classes:

The class diagram for .Net Interfaces

As you can see the IHierarchyItem is inherited by IFolder indirectly, IFolder inherits IItemCollection that adds GetChildernAsync method for enumerating folder content. In your code, you must implement IFolder and IFile interfaces and create your class derived from ContextAsync, that implements ContextAsync.GetHierarchyItemAsync method.

IHierarchyItem

IHierarchyItem interface contains properties and methods that are common for all WebDAV items. While all properties are self-explanatory here, let's look at specifics of their implementation.

Each item has its creation and modification date/time that your implementation must return in UTC. In the case of a file, the modification date must change only when the content of the file changes. It must not change when the item is locked or unlocked or properties modified. In particular Mac OS Finder relies on such behavior.

The path that you return from your Path property implementation should be relative to WebDAV root. For example, if your WebDAV server root is located at ‘http://webdavserver.com/myserver/’ and the item URL is ‘http://webdavserver.com/myserver/myfolder/myitem.doc’ the Path property implementation must return ‘myfolder/myitem.doc’. To calculate the entire item URL, the framework will call RequestAsync.ApplicationPath property and concatenate it with URL returned by Path property.

The Name property is ignored by Microsoft Web Folders client. To display files and folders names, it is using the last segment of the item URL, returned by the IHierarchyItem.Path property. When you are renaming an item in a WebDAV client, the IHierarchyItem.MoveToAsync method is called, there is no any separate method for renaming the item or changing the name. To avoid any inconsistency and achieve the same behavior within all WebDAV clients we recommend the Name property to be identical to the last segment of the URL. If you store this property separately from the URL, when the item is renamed using IHierarchyItem.MoveToAsync all, you must update this property.

Your implementation of CopyToAsync, MoveToAsync and DeleteAsync methods can update more than a single item in your repository. This usually happens when processing folder items. If an operation with some items failed, but you want to continue the operation, you can report the error to the client using multistate parameter passed to each of this methods. You can add error to the list calling MultistatusException.AddInnerException method. Note that while the engine will process all inner exceptions in MultistatusException and generate a WebDAV-compliant response with an error description for each failed item, many WebDAV clients, including Microsoft Mini-redirector client just ignores the response.

IContent

IContent interface provides methods and properties for reading and saving file content in your repository. We will start with ContentType property, as it is vital for web browser-based clients. Most WebDAV clients, including Web Folders/mini redirector, do not submit Content-Type header when uploading a file, so the ContentType parameter of the IContent.WriteAsync method will be null in most cases. Nevertheless, you still must provide the correct Content-Type / mime-type when it is requested by the WebDAV client. Browsers, such as Firefox, rely on the mime-type returned in Content-Type header to determine what action to take when the user clicks on file hyperlink. Depending on the mime-type provided by the server, the browser can either load file content in a browser window or can display File Open / File Save dialog. To get the file mime-type, the IT Hit WebDAV Server Engine for .Net provides MimeType class that returns mime-type by extension:

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

When WebDAV client is uploading a file to the server, the IFile.WriteAsync method is called. Most WebDAV clients including Microsoft Web Folders/mini-redirector, Mac OS X Finder, Microsoft Office and OpenOffice submit an entire file to the server in one piece. However, some WebDAV clients may want to update only a part of a file, submitting a file segment. To submit a file segment, the client application must attach the Content-Range: bytes XXX-XXX/XXX header to PUT request. If no Content-Range header is found the IT Hit WebDAV Server Engine assumes an entire file is submitted. The IFile.WriteAsync has parameters that provide information to implementers about position of the submitted segment inside content and total file size:

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The engine also supports file upload via multipart-encoded form using POST verb. In the case of POST upload, the IFile.WriteAsync also called, there is no any separate method required to implement for its processing.

Some WebDAV clients such as Mac OS X Finder upload content using chunked upload. There is no way to detect total content length in this case, and totalFileSize will be -1, in this case. As a general rule, we do not recommend relying on the totalFileSize parameter. For example, if a file over 2Gb is uploaded to IIS hosted server this parameter will be -1 as well.

For getting file content, the IFile interface provides ReadAsync method. The WebDAV client application can request either entire file or only a file segment. Unlike segmented upload, that is used rarely, the segmented download is often used by download managers. The client must attach Range header to indicate what file segment to download. Here is the example of IFile.ReadAsync implementation:

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

If your server is hosted in IIS/ASP.NET, to avoid content buffering on the server side make sure you set

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

To prevent script timeout increase it before file content writing or reading:

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

This code is part of WebDAV Server File System sample provided with IT Hit WebDAV Server Engine for .NET

                            
                        

The IFile.ContentLength must provide a length of the file. The value returned by this method is used in Content-Length header sent to a client as well as may be used by your hosting environment and engine. Sometimes you may need to encrypt content in your storage or somehow else modify it so that the actual content size on your storage changes. Make sure you correctly report the content, it must exactly specify amount of bytes sent to the client by your IFile.ReadAsync method implementation. If it is larger than the actual bytes sent, the request may hang, waiting for more bytes to be written by your code into the stream.

ETag property is the unique value that changes each time when you update file content. It is sent with GET request in the header and used by the client applications to determine if a file was changed since last read. Usually in your IFile.WriteAsync method implementation you must update ETag and file modification date.

IFile

The IFile interface represents a file in a repository. This is a marker interface derived from IContent and IHierarchyItem, it does not add any additional properties or methods.

IItemCollection

The IItemCollection interface extends IHierarchyItem interface and is implemented on container items, such as folders. It provides a single GetChildernAsync method, that is in the case of a folder, returns direct children of the folder:

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

The code below is part of 'WebDAVServer.FileSystemStorage.AspNet' C# & VB samples provided with the SDK.

                            
                        

When a user browses folders with WebDAV client the IItemCollection.GetChildernAsync method on folder items is always called. In this method, you will often filter items returned, depending on user permissions.

The propNames input parameter is provided solely for performance optimization purposes. It gives you a hint what properties were requested by the client application, so you can request all required properties for child items in a single call to your storage. Later engine will call IFolder and IFile methods and properties depending on the properties that were requested by the client.

The GetChildrenAsync() method also supports requesting a specified range of items and sorting. See Paging Through Folder Children Items article for more details.

IFolder

The IFolder interface is derived from IItemCollection. It adds 2 methods: CreateFileAsync and CreateFolderAsync, that are called when a file or a folder should be created. From CreateFileAsync call, you must return the object implementing IFile interface. Depending on the operation that WebDAV client requested, the engine may call IContent.Write to save file content, or call other file item methods.

 

Next Article:

Creating Class 2 WebDAV Server