📜 ⬆️ ⬇️

Preparing ASP.NET Core: how to present static content as resources

We continue our column on the topic ASP.NET Core by another publication from Dmitry Sikorsky ( DmitrySikorsky ) - the head of the company “Yubreinians” from Ukraine. In his article, Dmitry talks about the experience of working with static content in the form of resources outside the main project assembly in ASP.NET Core. Previous articles from the column can always be read on the link #aspnetcolumn - Vladimir Yunev
Sometimes it is necessary that static content (such as JS, CSS files or images) be located, for example, outside the main assembly of a web application as resources. In this small article I will talk about two approaches to solving this problem.

Preparing a project with resources


First, we need a project with resources. For example, let's add one CSS file to the resources (which will make all the text on the page red) and one picture. For this we need the files themselves, as well as, approximately the following line in the project.json file of our project:

"resource": [ "Styles/**", "Images/**" ] 

That's all, now after the project is assembled, the entire contents of the Styles and Images folders will turn into resources (obviously, you can specify really specific files, and not entire folders, if there is a need for this).
aspnetcolumngithub Tip! You can try everything yourself or by downloading the source code from GitHub https://github.com/DmitrySikorsky/AspNet5Resources .
By the way, when adding files to resources, the “tree” of their location becomes “flat”, and all the characters “\” in the file path turn into points. That is, the information about the original location is lost (given that file names may contain dots). For example, the file \ Styles \ test.css added to the resources in the AspNet5Resources.Resources project will have the following name (register has a value):
')
AspNet5Resources.Resources.Styles.test.css

Fortunately, we do not need to write the name of the assembly each time (in this case, AspNet5Resources.Resources) when retrieving content from resources. To do this, when creating the EmbeddedFileProvider, it is specified as the base namespace (see below).

Resource usage


We can use the content added to the project in the form of resources in at least two ways: implement everything yourself or use a ready-made implementation. Both ways are very simple.

To implement everything yourself, you need to add to the project using the content from the resources (it does not matter if the resources are located in this assembly or another), the controller that will extract the requested resources by their names and write them to the output stream:

 public class ResourceController : Controller { public ActionResult Index(string name) { Assembly assembly = Assembly.Load(new AssemblyName("AspNet5Resources.Resources")); string fullName = assembly.GetName().Name + "." + name; if (assembly.GetManifestResourceNames().Contains(fullName)) { Stream stream = assembly.GetManifestResourceStream(fullName); return this.Stream(stream); } return this.HttpNotFound(); } } 

To simplify the work with the output stream, our own StreamResult class, inherited from ActionResult, is used here:

 public class StreamResult : ActionResult { private Stream stream; public StreamResult(Stream stream) { this.stream = stream; } public async override Task ExecuteResultAsync(ActionContext actionContext) { HttpResponse httpResponse = actionContext.HttpContext.Response; await this.stream.CopyToAsync(httpResponse.Body); } } 

This is enough to be able to display a picture of the resources in this way:

 <img src="/resource?name=Images.test.png" /> 

Now we will use the out-of-the-box implementation.

First of all, we need to implement the IFileProvider interface so that, as a result, our class (let's call it CompositeFileProvider) can combine several different providers. The whole class can be viewed in the source code (link at the end of the article), but the key point is the following:

 public IFileInfo GetFileInfo(string subpath) { foreach (IFileProvider fileProvider in this.fileProviders) { IFileInfo fileInfo = fileProvider.GetFileInfo(subpath); if (fileInfo != null && fileInfo.Exists) return fileInfo; } return new NonexistentFileInfo(subpath); } 

That is, in fact, when searching for a file, our class simply goes through all the available providers in search of the one in which this file exists.

So that our application can use both physically existing files and files from resources, let's create an instance of our CompositeFileProvider in this way:

 public IFileProvider GetFileProvider(string path) { IEnumerable<IFileProvider> fileProviders = new IFileProvider[] { new PhysicalFileProvider(path) }; return new CompositeFileProvider( fileProviders.Concat( new Assembly[] { Assembly.Load(new AssemblyName("AspNet5Resources.Resources")) }.Select(a => new EmbeddedFileProvider(a, a.GetName().Name)) ) ); } 

Next, we need to "register" our provider when starting the application in the Startup class:

 public Startup(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment) { this.applicationBasePath = applicationEnvironment.ApplicationBasePath; hostingEnvironment.WebRootFileProvider = this.GetFileProvider(this.applicationBasePath); } 

After that, we can use content from resources in a more familiar way:

 <link href="/Styles.test.css" rel="stylesheet" /> 

findings


Personally, I prefer the second option, since it is more like the use of regular files (despite the fact that files are extracted from resources). If, for example, do not use dots in the file names, then you can even replace all the dots in the resource names, except the last one, with the “\” symbol and thus “restore” the original location and have a more visual URL, but this is not so important.

As always, I prepared a small test project so that you can immediately start everything and see with your own eyes: github.com/DmitrySikorsky/AspNet5Resources .

To authors


Friends, if you are interested in supporting the column with your own material, please write to me at vyunev@microsoft.com to discuss all the details. We are looking for authors who can interestingly tell about ASP.NET and other topics.

about the author


Sikorsky Dmitry Alexandrovich
Jubreynians Company (http://ubrainians.com/)
Owner, Head
DmitrySikorsky

Source: https://habr.com/ru/post/277235/


All Articles