ASP.NET Core MVC/WebApi Basic Series 1

Time:2019-9-4

> Preface

There seems to be a lot of EF Core published recently, but don’t mistake me for only specializing in EF Core. I’ve been watching ASP. NET Core in private, so I’ll talk about EF Core and ASP. NET Core later. Don’t think that you know ASP. NET Core very well by using ASP. NET Core. Even though it’s a basic series, you don’t know it either. Know ASP. NET Core.

UseStaticFiles、UseDefaultFiles、UseDirectoryBrowser、UseFileServer

When we create a default. NET Core Web application, by default. NET Core injects StaticFiles into our Web application to use static files in the wwwroot directory. Note that the injection of StaticFiles here is based on static files in the wwroot directory. Here we enable the default static files by using DefaultFiles.


app.UseDefaultFiles();
app.UseStaticFiles();

 

Before that, we created four static HTML files in the wwwroot directory, as follows:

According to the official documents, we create the four static HTML as above, and also find the default. HTM in the wwwroot directory according to the above order. So if we display the corresponding content at this time, if we delete the first html, we will find default. html, and so on. What if we reversed the injection sequence? As follows:


app.UseStaticFiles();
app.UseDefaultFiles();

Page 404 can’t find the page. Why? The official document emphasizes that the injection of default files must be placed before the injection of static files, mainly because the injection of default files is just a URL rewrite, telling the route that I want to find the static files in the wwwroot directory, but in fact the static files are provided by StaticFiles, so that’s why the injection of default files is necessary. Place it before injecting static files. But what if we had to put the injection default file before the injection static file? Next, by using UseFileServer, UseFileServer is a combination of UseDefaultFiles and UseStaticFiles. Since it is a combination, let’s put UseFileServer first. Let’s try it, as follows:


app.UseFileServer();
app.UseStaticFiles();
app.UseDefaultFiles();

The result will be static HTML by default. I won’t show it here anymore. Interested children’s shoes can be studied by themselves. Next, let’s look at enabling directory browsing, which is the same as enabling directory browsing on IIS, as follows:


app.UseDirectoryBrowser();
app.UseFileServer();
app.UseStaticFiles();
app.UseDefaultFiles();

I don’t need to repeat that here, but the question arises: What if we put directory browsing enabled after using MVC routing? Does enabling directory browsing override MVC routing? No, it can be verified by itself.


app.UseMvc(routes =>
 {
 routes.MapRoute(
 name: "default",
 template: "{controller=Home}/{action=Index}/{id?}");
 });
 
 app.UseDirectoryBrowser();

 Customize default file directory

On the basis of modifying the default file name and so on, the official document has detailed instructions, here is no longer a demonstration, waste space, let’s focus on different. For example, static files are enabled by default in the wwwroot root directory. What if we want to put static files in the dist folder in the first picture given? What should we do now? Because. NET Core defaults to the wwwroot directory as a static file directory, we need to change its directory to the dist directory under the wwwroot directory at this time. By using the UseWebRoot method, we can change the Web static directory to the dist directory under the wwwroot directory, while enabling the default files (UseDefaultFiles) as follows:


.UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "dist"))

 

So the question arises again. Suppose I want to put the default static file outside the project root directory, what should we do at this time? For example, access the following static HTML files.

At this point, we can use the overload of the UseDefaultFiles method to replace the directory to the OutDefaultHtml directory under the project root directory, as follows:


var fileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, 
 "OutDefaultHtml"));

 app.UseDefaultFiles(new DefaultFilesOptions()
 {
 FileProvider = fileProvider,
 DefaultFileNames = new [] { "OutDefault.html" }
 });

Because we changed the directory to find static HTML and the default file was UseStaticFiles, we also need to overload the switch directory through the UseStaticFiles method instead of wwroot, as follows:


app.UseStaticFiles(new StaticFileOptions()
 {
 FileProvider = fileProvider
 });

Is there a more concise way to use UseDefaultFiles and UseStaticFiles in addition to the above? Of course, when the default static file is no longer in the wwwroot directory to meet our needs, we need to customize the directory where the default static file is placed, we recommend the use of the combination of the two, namely UseFileServer. The above can be amended as follows:


var fileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath,"OutDefaultHtml"));
 var fileServerOptions = new FileServerOptions();
 fileServerOptions.DefaultFilesOptions.DefaultFileNames = new[] { "OutDefault.html"};
 fileServerOptions.FileProvider = fileProvider;

 app.UseFileServer(fileServerOptions);

 Detailed UseStatic Files

In most cases, we put static files in the wwwroot directory, but in 10% of cases, static files will be placed in the project root directory, then the default injection of UseStaticFiles method is no longer applicable, and then we need to use its overload method. For example, if we want to visit mvc_course.gif in the following figure, what should we do?

As mentioned above, we need to use the UseStaticFiles method to overload. The first parameter is to switch the directory to the directory where the static file is located. The second parameter is to use the virtual path to access the static file. In order not to expose the actual physical path, as follows:

app.UseStaticFiles(new StaticFileOptions()
 {
 FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "OutStaticFiles")),
 RequestPath = "/outfiles"
 });
 
 perhaps

 //app.UseStaticFiles(new StaticFileOptions()
 //{
 // FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "OutStaticFiles")),
 // RequestPath = new PathString("/outfiles")
 //});

This overloading method also has a delegation parameter OnPrepareResponse, which is mainly used to cache static files. Next, we will focus on it. In fact, this article is the focus. Haha, simple people go directly to the official Internet cafe.


app.UseStaticFiles(new StaticFileOptions()
 {
 FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "OutStaticFiles")),
 RequestPath = "/outfiles",
 OnPrepareResponse = ctx => 
 {
 const int cacheControll = 60;
 ctx.Context.Response.Headers["Cache-Control"] = "public,max-age=" + cacheControll;
 }
 });

In fact, the API of the official document is outdated. For the setting of the request header, there is an enumeration such as Header Names, which is no longer set in the form of strings. This is not easy to make mistakes and convenient. There are two kinds of caching control settings in the request header. It can be done in any way.

What is the practical effect of adding cache control to the response header? At this point, we will talk about the principle of cache control. The expiration time of the above cache control settings is 60 seconds. When the first request is returned to 200, repeated refreshes within 60 seconds will be 200, and read from the browser cache. Once 60 seconds have passed, the refresh will then go to read the image on the server. It is found that the image has not changed and returned to 304 unchanged. So the question arises, if we change the content of the picture in this gap, and then refresh the content of the picture will change? The answer is: No, as long as we modify the content of the image during the buffer gap, and then refresh or display the original image (unless Ctrl + F5 is forced to refresh). Okay, so let’s go ahead and look at the TagHelper feature in ASP. NET Core: the asp-append-version feature. Is this characteristic the same as the principle of cache control? Next, let’s talk about asp-append-version and its principle.

Detailed description and principle of asp-append-version

Let’s take the picture in image file under wwroot directory as an example, then visit the picture on the page and add asp-append-version to see, as follows:


<img src="~/images/mvc_course.gif" asp-append-version="true" />

 

At this point, the response return link address is http://localhost:63277/images/mvc_course.gif?V=y3F-lvD7XoqGqLIWq_WsuFN9POPSjit1Au6_0iRrgwE. As can be seen from the figure above, at this time, we add a version number V after the image, we refresh the string after the version number repeatedly has not changed, so this is similar to Ha-ha. How did the value of the Greek code come from? The hash code, or version number, is calculated based on the request URL and image content. That is to say, as long as we change the content of the picture, when we refresh or re-visit this page, the content will be updated accordingly, which is what we call cache breakdown. Compared with cache control, as long as we change the content of the picture in the buffer gap time, unless forced refresh, the picture will still appear. The old picture, and the asp-append-version feature is that you change, I change, you change, I remain the same. Is it that simple? Next, let’s look at the images in the project root directory, overload the external images through UseStatic Files, and add the asp-append-version feature.


<img src="/outfiles/mvc_course.gif" asp-append-version="true" />

WOW, see what or not, find what or not, so we can conclude that asp-append-version features to achieve image caching only for static files in the WebRoot directory, while external static files are invalid.

Now that the problem has been highlighted, asp-append-version is mainly aimed at static files in the WebRoot directory, while only wwroot is in the WebRoot directory, so we can call it only static files in the wwroot directory, so can we try to put the external file directory in the WebRoot directory? What about caching external static files? Let’s try to inject UseStatic Files into Startup. CS by default. We leave it on hold so that the default styles, scripts, and files for wwwroot will not change. We just inject a UseStatic Files again, as follows:


var compositeProvider = new CompositeFileProvider
 (
 env.WebRootFileProvider,
 new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "OutStaticFiles"))
 );
 env.WebRootFileProvider = compositeProvider;
 app.UseStaticFiles(new StaticFileOptions()
 {
 FileProvider = compositeProvider,
 RequestPath = "/outfiles"
 });

 

As mentioned above, for the default WebRoot, wwwroot, we add an external directory to it as a composite FileProvider as WebRoot, so that everything remains unchanged. Let’s make the following visit again.


<img src="/mvc_course.gif" asp-append-version="true" />

If you access the static files as a WebRoot directory for OutStaticFiles, you can never add the virtual path of outfiles, so as to be an external static file, so that no version number will appear. The results are as follows:

How can we customize the effect of adding a version number similar to the asp-append-version feature to external files? We have clearly explained that the essence of asp-append-version is to calculate the version number based on the request URL and the content of the request picture to achieve caching. For caching, we can use the IMemoryCache interface to cache. The path of the request can be obtained through the request context, but also through environmental change. Quantity gets to the directory where the request static file is located, so we just need to implement the view expansion method next.

The view extension method points to the IRazorPage interface, and then the parameters are our file path. ASP. NET Core has dependency injection, which makes us very happy. We get the request context through the view context in the view. Then we get the injected IMemoryCache and IHostingEnviroment interfaces. ASP.NET Core gives us the FileVersionProvider class for the file version number, as follows:

We pass the parameters to the FileVersionProvider constructor, and finally add the resulting version number to the tail of the file path we requested, as follows:


public static class RazorPageExtension
 {
 public static string AddAppendVersion(this IRazorPage page, string path)
 {
 var context = page.ViewContext.HttpContext;

 var memoryCache = context.RequestServices.GetService(typeof(IMemoryCache)) as IMemoryCache;

 var hostingEnviroment = context.RequestServices.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;

 var fileversionProvider = new FileVersionProvider(hostingEnviroment.WebRootFileProvider, memoryCache, context.Request.Path);

 return fileversionProvider.AddFileVersionToPath(path);
 }
 }

We use the Razor view extension method of the above custom implementation to access the picture and get the version number. The following is a try:


<img src="@this.AddAppendVersion("/mvc_course.gif")" 

summary

This paper explains in detail the static files in ASP.NET Core MVC and the essential principles of cache control and asp-append-version. It also explains the differences between cache control and asp-append-version. By default, asp-append-version is only valid for wwwroot, because only wwroot exists in WebRoot. To be valid for external files, the directory of external files can also be used as WebRoot.

Recommended Today

Hadoop MapReduce Spark Configuration Item

Scope of application The configuration items covered in this article are mainly for Hadoop 2.x and Spark 2.x. MapReduce Official documents https://hadoop.apache.org/doc…Lower left corner: mapred-default.xml Examples of configuration items name value description mapreduce.job.reduce.slowstart.completedmaps 0.05 Resource requests for Reduce Task will not be made until the percentage of Map Task completed reaches that value. mapreduce.output.fileoutputformat.compress false […]