Search code examples
c#httpowinkatana

Modify OWIN/Katana PhysicalFileSystem page on request


I have a self-hosted app that's using OWIN to provide a basic web server. The key part of the configuration is the following line:

appBuilder.UseFileServer(new FileServerOptions {
    FileSystem = new PhysicalFileSystem(filePath)
});

This provides the static files listed in the filePath for browsing, and this much is working as expected.

However I've run into a case where I want to slightly modify one of the files on a request-by-request basis. In particular, I want to load the "normal" version of the file from the filesystem, alter it slightly based on the incoming web request's headers, and then return the altered version to the client instead of the original. All other files should remain unmodified.

How do I go about doing this?


Solution

  • Well, I don't know if this is a good way to do this, but it seems to work:

    internal class FileReplacementMiddleware : OwinMiddleware
    {
        public FileReplacementMiddleware(OwinMiddleware next) : base(next) {}
    
        public override async Task Invoke(IOwinContext context)
        {
            MemoryStream memStream = null;
            Stream httpStream = null;
            if (ShouldAmendResponse(context))
            {
                memStream = new MemoryStream();
                httpStream = context.Response.Body;
                context.Response.Body = memStream;
            }
    
            await Next.Invoke(context);
    
            if (memStream != null)
            {
                var content = await ReadStreamAsync(memStream);
                if (context.Response.StatusCode == 200)
                {
                    content = AmendContent(context, content);
                }
                var contentBytes = Encoding.UTF8.GetBytes(content);
                context.Response.Body = httpStream;
                context.Response.ETag = null;
                context.Response.ContentLength = contentBytes.Length;
                await context.Response.WriteAsync(contentBytes, context.Request.CallCancelled);
            }
        }
    
        private static async Task<string> ReadStreamAsync(MemoryStream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(stream, Encoding.UTF8))
            {
                return await reader.ReadToEndAsync();
            }
        }
    
        private bool ShouldAmendResponse(IOwinContext context)
        {
            // logic
        }
    
        private string AmendContent(IOwinContext context, string content)
        {
            // logic
        }
    }
    

    Add this to the pipeline somewhere before the static files middleware.