咱们在优化Web服务的时候,对于静态的资源文件,一般都是经过客户端缓存、服务器缓存、CDN缓存,这三种方式来缓解客户端对于Web服务器的链接请求压力的。css
本文指在这三个方面,在ASP.NET Core中静态文件的实现过程和使用方法进行阐述。固然也能够考虑使用反向代理的方式(例如IIS或Nginx),这些不是本文讨论的内容。git
本文重点展现如何经过StaticFileMiddleware中间件,提升网站的性能。虽然这不是惟一缓存文件的方式,咱们还能够经过ResponseCacheAttribute特性为ASP.NET Core Mvc的Controller和Action进行缓存的设置。github
当建立一个ASP.NET Core的项目时,查看Startup.Configure方法,就会看到默认模板生成的添加StaticFileMiddleware中间件的方法。ajax
public void Configure(IApplicationBuilder app) { // looging and exception handler removed for clarity app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
这样就使你的应用程序可以处理,程序目录下wwwroot目录的静态文件内容。在咱们添加文件缓存以前,咱们先要看一下StaticFileMiddleware默认的策略是怎么样的。当第一次加载程序时,浏览器将打开页面并下载全部的资源链接。假如页面没有错误返回都是正确那么就是返回文件数据和Http Status为200 -OK的状态。bootstrap
而后咱们看下这个Http请求对应的Response Header,这里会包含ETag和Last-Modified两个值。HTTP内容以下:浏览器
HTTP/1.1 200 OK Date: Sat, 15 Oct 2016 14:15:52 GMT Content-Type: image/svg+xml Last-Modified: Sat, 15 Oct 2016 13:43:34 GMT Accept-Ranges: bytes ETag: "1d226ea1f827703" Server: Kestrel
若是再次请求这个地址的话,浏览器将发送ETag和Last-Modified的值到服务端,若是两个值没有变化,那么服务端会发送304状态到浏览器,那么浏览器将使用以前的资源而不是从新下载一份。缓存
这样就提升了,浏览器的响应性能,由于文件都缓存到了客户端,而且经过304状态,服务端与浏览器的请求流量得以减小。服务器
固然咱们都知道若是要设置某一请求的缓存,只须要设置Header为Cache-Control的值。那么在StaticFileMiddleware中间件中,咱们怎么设置这个Header呢?app
using Microsoft.Net.Http.Headers; app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => { const int durationInSeconds = 60 * 60 * 24; ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + durationInSeconds; } });
设置后每个静态文件的请求都会执行这个方法,包括200和304状态的请求;并且在这个例子里浏览器会自动缓存这些文件24小时,可是在此期间并不会返回404状态。ide
一旦max-age设置的时间过时,浏览器就不会再使用本地缓存,而去直接请求服务器端。这样已经避免了一些额外的请求到服务器端了。若是咱们在浏览器与服务器中间使用CDN缓存文件数据的话,这样就算客户端浏览器的缓存过时了,可是请求也不会到咱们的服务器上,而是请求到CDN缓存服务器。
下面咱们看看文件缓存在ASP.NET Core中是如何判断缓存失效的?.NET Core开源的代码为咱们提供了了解它的入口【代码 Source Code】。核心代码以下:
_length = _fileInfo.Length; DateTimeOffset last = _fileInfo.LastModified; // Truncate to the second. _lastModified = new DateTimeOffset(last.Year, last.Month, last.Day, last.Hour, last.Minute, last.Second, last.Offset).ToUniversalTime(); long etagHash = _lastModified.ToFileTime() ^ _length; _etag = new EntityTagHeaderValue('\"' + Convert.ToString(etagHash, 16) + '\"');
服务器端若是检测到文件改变就会返回200状态给浏览器,若是没有变化则返回304状态给浏览器端。
不幸的是,一旦咱们添加了缓存,浏览器将再也不向服务器发出请求。该文件可能已经彻底改变或已被彻底删除,但若是浏览器不要求,服务器将不能通知浏览器从新发起无缓存的请求!
一般咱们都使用形如https://localhost/js/site.js?v=1 这样的地址来解决缓存的问题。经过给静态文件生成惟一的版本号,作为QueryString进行请求时,服务器将从新输出文件内容。
在ASP.NET Core中Tag Hepers为咱们提供了这样的API:
<script src="~/js/site.js" asp-append-version="true"></script>
这段代码最终在浏览器端会被渲染为以下Html代码:
<script src="/js/site.js?v=Ynfdc1vuMNOWZfqTj4N3SPcebazoGXiIPgtfE-b2TO4"></script>
若是静态文件发生改变,Tag Helper就是从新计算文件的哈希值,它采用 SHA256的哈希值。固然之前服务器缓存的文件版本也随之失效了。这个asp-append-version Tag Helper能够支持Img、Script和Link元素。
ASP.NET Core的源代码咱们来看看是怎么计算文件变化的:【源代码 Source Code】 。
咱们在使用CDN时,由于还要进行开发任务,通常咱们都要有两套地址,一套是CDN上的文件地址,一套是本地调试开发用的地址。ASP.NET Core中也为咱们提供了Tag Helper来解决这样的问题。直接上代码实例吧:
<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css" asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css" asp-fallback-test-class="hidden" asp-fallback-test-property="visibility" asp-fallback-test-value="hidden" /> <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js" asp-fallback-src="~/lib/bootstrap/js/bootstrap.min.js" asp-fallback-test="window.jQuery"> </script>
Tag Helper:asp-fallback-* 解决开发时使用的文件地址问题。 固然它也能够asp-append-version 两个Tag Helper一块儿使用,这样就实现了,在CDN文件缓存的同步问题。
新的ASP.NET Core为咱们提供了不少现有互联网行业的解决方案,也给.NET开发人员引入了先进思想。
GitHub:https://github.com/maxzhang1985/YOYOFx 若是觉还能够请Star下, 欢迎一块儿交流。
.NET Core 和 YOYOFx 的交流群: 214741894