I would like to expose an ASP.Net Web Api 2 action using the HTTP PUT verb to upload files. This is coherent with our REST model as the API represents a remote file system (
After a few tests it seems the server-side code I posted as an example is correct. Here is an example, stripped out of any authentication/authorization/error handling code:
[HttpPut]
[Route(@"api/storage/{*resourcePath?}")]
public async Task<HttpResponseMessage> PutFile(string resourcePath = "")
{
// Extract data from request
Stream fileContent = await this.Request.Content.ReadAsStreamAsync();
MediaTypeHeaderValue contentTypeHeader = this.Request.Content.Headers.ContentType;
string contentType =
contentTypeHeader != null ? contentTypeHeader.MediaType : "application/octet-stream";
// Save the file to the underlying storage
bool isNew = await this._dal.SaveFile(resourcePath, contentType, fileContent);
// Return appropriate HTTP status code
if (isNew)
{
return this.Request.CreateResponse(HttpStatusCode.Created);
}
else
{
return this.Request.CreateResponse(HttpStatusCode.OK);
}
}
A simple console app is enough to test it (using Web Api client libraries):
using (var fileContent = new FileStream(@"C:\temp\testfile.txt", FileMode.Open))
using (var client = new HttpClient())
{
var content = new StreamContent(fileContent);
content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
client.BaseAddress = new Uri("http://localhost:81");
HttpResponseMessage response =
await client.PutAsync(@"/api/storage/testfile.txt", content);
}
Edit 2018-05-09:
As stated in this comment, if you plan to support file names with an extension ({filename}.{extension}
) without forcing the client to append a trailing slash, you will need to modify your web.config to bind IIS to your web api application for these file types, as by default IIS will use a static file handler to handle what looks like file names (i.e. URLs with the last path segment containing a dot). My system.webServer
section looks like:
<system.webServer>
<handlers>
<!-- Clear all handlers, prevents executing code file extensions or returning any file contents. -->
<clear />
<!-- Favicon static handler. -->
<add name="FaviconStaticFile" path="/favicon.ico" verb="GET" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
<!-- By default, only map extensionless URLs to ASP.NET -->
<!-- (the "*." handler mapping is a special syntax that matches extensionless URLs) -->
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<!-- API endpoints must handle path segments including a dot -->
<add name="ExtensionIncludedUrlHandler-Integrated-4.0" path="/api/storage/*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
</system.webServer>
Note that some file names will not be possible because of various limitations. For example you can't name a path segment .
or ..
because the RFC requires to replace it, Azure hosting services won't allow a colon as the last character of a path segment, and IIS forbids a set of characters by default.
You may also want to increase IIS / ASP.NET file upload size limits:
<!-- Path specific settings -->
<location path="api/storage">
<system.web>
<httpRuntime maxRequestLength="200000000" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="200000000" />
</requestFiltering>
</security>
</system.webServer>
</location>