Miniweb asp.net core

Embed assets in your assembly and serve them on runtime

View project on GitHub

This used to require some work, but...

But now we can just create just use a Razor Class Library. The old way used to work fine as well, will leave it here for reference if needed:


Embed assets in your assembly

In MiniWeb the MiniWeb.Core package has 4 files that are served with the project within the assembly as embedded files.

To embed assets into you assembly/package you need to do two things:

  • first register the assets in the project.json
  • serve the assets when requested

Register assets in project.json

To register you assets as "embedded" into your assembly add the following lines to your project.json file

"resource": "Resources/admin.js;Resources/admin.css;Resources/bootstrap-wysiwyg.js;Resources/adminview.cshtml",

Here four files are embedded into the assembly on build which can then be read on runtime from the assembly

You can use file globbing inhere to register whole folders if needed, but as in this example you can just register the files needed for your project

Serve the assets when requested

To serve the assets when requested you need to write a Custom Middleware layer that is registered (in MiniWeb's case) before the "serve all" Mvc route that handles everything.

This Custom Middleware should handle the requests to a custom path you monitor that handles the embedded files. Let's say "miniweb-embedded" in this case. The Custom Middleware should then implement the Invoke(HttpContext context) method and check for the path and then serve the request.

In the project.json you need to register a dependency on "Microsoft.AspNet.FileProviders.Embedded"

using ....
using Microsoft.AspNet.FileProviders;// Needed to read the embedded files

public class CustomMiddleware
{
    public Invoke(HttpContext context)
    {
        var path = context.Request.Path.Value;
        if (path.StartsWith("/miniweb-embedded/"))
        {
            var provider = new EmbeddedFileProvider(this.GetType().GetTypeInfo().Assembly, 
                                                    this.GetType().Namespace);
            var filename = Path.GetFileName(path);
            
            var fileInfo = provider.GetFileInfo($"Resources/{filename}");

            if (fileInfo.Exists)        
            {
                using (var stream = fileInfo.CreateReadStream())
                {
                    var reader = new StreamReader(stream);
                    await context.Response.WriteAsync(reader.ReadToEnd());
                }                
            }
            else
            {
                context.Response.StatusCode = 404;
            }
        }
        else
        {
            await _next(context);
        }
    }
}

Here the embedded resource is requested when the path starts with the "/miniweb-embedded/" path and it does the following

  • Check the path if it starts with "/miniweb-embedded/"
  • Create a EmbeddedFileProvider for the current assembly, make sure this is the same assembly the resources are embedded in ofcourse. Also this is best done in the MiddleWare constructor only once so the provider can be reused
  • Try to get the file from the embedded resources, in this example the resources were in the folder Resrouces/.. internally so append the filename to that folder and get the FileInfo object
  • If the FileInfo object returns and Existing file then return the response. In this case you should maybe also set the appropriate headers like ContentType and Cache headers and such
  • Otherwise return a 404 exception
  • If the path did not start with the "/miniweb-embedded/" folder pass the call on to the next in line

Remarks

To use this middleware you need to register it in the Startup Configure call which is done like this:
public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<CustomMiddleware>();
}

If you want to embed a Razor View this process is not enough, because Razor Views are compiled on the fly on startup. To support this you need to setup a FileProvider on the RazorViewEngineOptions in the ConfigureServices Startup method. This will be explained in another page

Thanks,

Rooc