ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存

news/2024/5/22 4:04:37/文章来源:https://blog.csdn.net/weixin_30830327/article/details/96019725

分享

最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了。

背景

在项目开发的过程中,为了满足需求,还是有许多功能要自己“发明”,也就是已有技术的组(qi)合ji)运(yin)用(qiao)。本例先讲讲如果用中间件开发所有CMS都需要的服务端静态缓存方法。

CMS系统的一大痛点是一个页面要查询的内容很多,所以常常为了减轻服务器压力,都会使用到各种缓存技术。比如静态文件的CDN缓存、客户端缓存、还有就是服务端静态化缓存。

服务端静态化缓存在这里指的是把页面事先生成出来保存为静态文件,当用户请求服务器时,可以直接把页面输出给用户,而不再进行查询数据库之类的操作,已达到提高响应速度、减轻服务器压力的效果。

原理

由于服务端静态化缓存的实现核心是需要拦截请求并且直接返回HTML内容,在MVC5时代,我们可以用过滤器(Filter)来处理,类似如下代码:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]public class StaticFileHandlerFilterAttribute : ActionFilterAttribute{/// <summary>/// 过期时间,以小时为单位/// </summary>public int Expiration { get; set; }public override void OnResultExecuted(ResultExecutedContext filterContext){var actionResult = filterContext.Result;if (actionResult is ViewResult){var fileInfo = GetFileInfo(filterContext);if (fileInfo.Exists && fileInfo.CreationTime.AddHours(Expiration <= 0 ? 1 : Expiration) > DateTime.Now)return;if (fileInfo.Exists){fileInfo.Delete();}using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.CreateNew, FileAccess.Write, FileShare.None)){using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)){var viewResult = actionResult as ViewResult;var viewContext = new ViewContext(filterContext.Controller.ControllerContext, viewResult.View, viewResult.ViewData, viewResult.TempData, sw);viewResult.View.Render(viewContext, sw);}}}}public override void OnActionExecuting(ActionExecutingContext filterContext){var fileInfo = GetFileInfo(filterContext);if (fileInfo.Exists){using (FileStream fs = File.Open(fileInfo.FullName, FileMode.Open)){using (StreamReader sr = new StreamReader(fs, Encoding.UTF8)){ContentResult contentresult = new ContentResult();contentresult.Content = sr.ReadToEnd();contentresult.ContentType = "text/html";filterContext.Result = contentresult;}}}}}

而在ASP.NET Core中,我们也可以在Filte中实现,但是,更方便、更牛、更快的方式就是在中间件中实现了。

中间件示意图

中间件运行在请求管道中,个人理解是可以看成是一个递归方法,只不过是调用不同的中间件,中间件中调用下一个中间件、知道最后一个执行完成,会沿路返回上一个中间件……说得可能不清楚,但是看代码和调试一下就能明白了。下面用代码层错误拦截的中间件作为一个中间件生命周期的示例:

    app.Use(async (context, next) =>{try{//执行下一个中间件前执行的代码await next();//上一个中间件执行后执行}catch (Exception ex){context.Response.StatusCode = 500;}//执行完后会继续执行上一个中间件next委托之后的语句});

中间件是写在Startup.cs文件的Configure方法中,以app.Use()方法的执行顺序添加中间件,然后会从上往下运行,遇到next委托就会跳入下一个中间件,不执行next委托就会返回上一个中间件。

好了,关于中间件的具体介绍可以看官方文档

实现服务端静态化缓存中间件

下面介绍思路,实现静态化缓存的核心是需要从请求响应中获取内容,并且转换为字符串保存到文件中

这个功能点用以下代码和注释进行讲解:

//获取响应体的引用
var originalBody = context.Response.Body;try
{//因为响应体实例是只读的,需要创建一个内存流实例,用来获取响应流内容using (var memStream = new MemoryStream()){//把内存流的引用设置到Response.Body,假装是真的响应体接收数据context.Response.Body = memStream;//然后执行下一个中间件,等待有响应返回await next();//需要判断响应是否正确,总不能把不正确的内容缓存起来吧if (context.Response.StatusCode == (int)HttpStatusCode.OK){//检测文件存放目录if (!Directory.Exists(filePath))Directory.CreateDirectory(filePath);//把内存流的当前操作位置设为0,因为响应写入的过程中位置会在末尾。memStream.Position = 0;//读取内存流的内容,转换为字符串var responseBody = new StreamReader(memStream).ReadToEnd();//把字符串写入文件,这里还稍微压缩了一下await File.WriteAllTextAsync(fullPath, Regex.Replace(responseBody, "\\n+\\s+", string.Empty));//在此把内存流的当前操作位置设为0memStream.Position = 0;//还需要把流复制到之前引用的响应体实例await memStream.CopyToAsync(originalBody);}}
}
finally
{//把响应体实例引用重新设置到响应体context.Response.Body = originalBody;
}

好了,这样响应的内容终于可以缓存起来了,下次请求直接把静态文件输出就是了。但是,好像还有问题:

  1. 如何对特定的请求的url做缓存;
  2. 如何设置过期时间。

解决这两个问题的关键就在文件名上了,对于第一点很容易理解,直接把url作为文件名不就完了。没错,但是要注意特殊字符的问题,我这里就用md5处理一下了,这样相同的请求url就会返回相同的静态文件里的内容。

但是网站要更新,不能永久都是一样的数据呀,这时就需要设置缓存时间了。这里又会产生两个问题:

  1. 如何在文件上标识产生缓存时的时间;
  2. 如何判断时间是否超时。

针对这两个问题,我也考虑了很久,总是觉得直接在文件名上记录缓存时间和判断超时就好了,这也有两种方法:

  1. 读取文件名上的时间,与当前时间比对;
  2. 在超时时间内都能产生相同的文件名,判断是否存在这个文件。

从代码简洁的考虑上,我选择挑战难度大一点的第二个方法。那么如何使程序在超时时间内都产生相同的文件名呢?其实只需要实现一个算法,在一段时间内产生的时间对象都是相同的时间,忽略中间的时间变化。这个是不是很像小学数学求近似数里的去尾法?或者我们经常用到的"/"运算符,会吧小数点去掉。而在本例中,实现利用整除发忽略一段时间中的时间变化,生成同一个时间的算法和代码如下:

       var timeTicks = new DateTime(DateTime.Now.Ticks / 10000000 / expire * 10000000 * expire);

哈哈,只要一行代码,整除去掉多余的时间再乘回来构造一段时间内不变的时间。其实运行一下可以发现,如果超时时间设置成60秒,那么单位秒上的值会变成00。设置为3600秒,那么分秒两个单位都是0,因此会有一个弊端,真正缓存的时间很可能比设置的值短的,这个要看需求的容忍度啦!

就这样,问题一个个被解决,下面给出完整的中间件代码:

    var hasExpire = int.TryParse(Configuration["html_cache_expire_time"], out var expire);if (hasExpire && expire > 0){//文件缓存app.Use(async (context, next) =>{var url = context.Request.Path.ToString();var th = new MD5CryptoServiceProvider();var data = th.ComputeHash(Encoding.Unicode.GetBytes(url));var key = Convert.ToBase64String(data, Base64FormattingOptions.None);var path = HttpUtility.UrlEncode(key);var timeTicks = new DateTime(DateTime.Now.Ticks / 10000000 / expire * 10000000 * expire);const string filePath = "static/cache/";var fileName = path + "." + timeTicks.ToString("yyyyMMddHHmmss") + ".html";var fullPath = Path.Combine(filePath, fileName);if (File.Exists(fullPath)){await context.Response.SendFileAsync(fullPath);}else{var originalBody = context.Response.Body;try{using (var memStream = new MemoryStream()){context.Response.Body = memStream;await next();if (context.Response.StatusCode == (int)HttpStatusCode.OK){if (!Directory.Exists(filePath))Directory.CreateDirectory(filePath);memStream.Position = 0;var responseBody = new StreamReader(memStream).ReadToEnd();await File.WriteAllTextAsync(fullPath, Regex.Replace(responseBody, "\\n+\\s+", string.Empty));memStream.Position = 0;await memStream.CopyToAsync(originalBody);}}}finally{context.Response.Body = originalBody;}}});}

反思

其实这个中间件还是有很多改进的地方,比如定义一套规则来给不同页面设置不同的超时时间,这样有的需要实时更新的内容就可以被区分开,而基本不变的内容就可以一直使用缓存。

转载于:https://www.cnblogs.com/ElderJames/p/A-Middleware-Implement-For-Server-Side-Static-Caching-In-AspNetCore.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_739020.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计师工资低?10大网站助你快速涨1万身价

设计师中如何快速涨1万身价&#xff0c;这个是每个设计师都非常关注的问题&#xff0c;小编认为想要快速提升自己的身价&#xff0c;高效的工作效率是必不可少的。科学有效地提高单位时间内的工作效率&#xff0c;短期来看可以轻松避免不必要的加班&#xff0c;对于设计师个人和…

jira无法访问此网站_企业制作营销型网站时需注重哪些问题

相信所有企业在进行营销型网站建设时&#xff0c;都希望自己的网站能够达到最好的效果&#xff0c;这样企业才能在互联网当中获得更大的效果。但是在实际当中&#xff0c;虽然制作营销型网站的企业成千上万&#xff0c;然能够具备这样效果的网站却是微乎其微&#xff0c;最根本…

分享10个高质量纹理背景资源网站

纹理是网页设计中非常有用的视觉元素&#xff0c;常用于网站背景。今天&#xff0c;这篇文章与大家分享10个高质量纹理背景资源网站&#xff0c;在这些网站中有各种风格的背景资源&#xff0c;相信会有你喜欢的。 DinPattern Pattern8 Colorburned Alice Grafixx Brusheezy Pat…

Sharepoint学习笔记---如何在Sharepoint2010网站中整合Crystal Report水晶报表(显示数据 一)...

公司上马新的ERP系统&#xff0c;需要把以前开发的水晶报表结合新的ERP系统整合到Sharepoint2010中&#xff0c;以前曾经尝试在Sharepoint2010中整合水晶报表&#xff0c;但由于当时SAP公司还没推出64bit系统的相关Package&#xff0c;而我们的开发环境和生产环境均为Windows20…

网站服务器如何处理客户端请求,实际的Web服务器处理客户端请求的步骤分析

第一步——接受客户端连接接受一个客户端连接&#xff0c;或者如果不希望与这个客户端建立连接&#xff0c;就将其关闭。第二步——接收请求报文从网络中读取一条HTTP请求报文解析报文1. 解析请求行&#xff0c;查找请求方法、指定的资源标识符(URI)以及版本号&#xff0c;各项…

抓取一个连续的网页_做了这么久SEO优化,想必你很了解“网页快照”

在网站做SEO优化时&#xff0c;我们经常能接触到一个关键词就是“网页快照”&#xff0c;那么什么是网页快照呢?当搜索引擎在收录网站时&#xff0c;会对网页进行备份&#xff0c;存在自己的服务器缓存里&#xff0c;当用户在搜索引擎中点击“网页快照”链接时&#xff0c;搜索…

为什么wifi有些网站进不去_有些网站为什么不能访问

很多人会问一个简单的问题,网址失效,网站访问不了,网站打不开了,网站屏蔽了。 简单说一下网站打不开的几点问题,网站打不开一般分为三种: 1、网站关闭打不开 2、网站被Q打不开 3、网站被浏览器监测屏蔽打不开 其中第1是你如何都打不开的,因为网站关闭,网络上就不存在这…

网站镜像服务器,什么是网站镜像,什么又是恶意网站镜像?

原标题&#xff1a;什么是网站镜像&#xff0c;什么又是恶意网站镜像&#xff1f;什么是网站镜像&#xff1f;什么又是恶意网站镜像&#xff1f;什么是网站镜像 网站镜像指的是把相同的数据存放在不同的服务器&#xff0c;镜像网站可以及时同步主站数据&#xff0c;像主站的一面…

php 获取其他页面的cookie_cookie的网站统计相关知识

什么是 Cookie&#xff1f;Cookie 是您访问过的网站创建的文件&#xff0c;用于存储浏览信息&#xff0c;例如您的网站偏好设置或个人资料信息。共有两种类型的 Cookie&#xff1a;第一方 Cookie 是由地址栏中列出的网站域设置的 Cookie&#xff0c;而第三方 Cookie 来自在网页…

php用ajax对seo,原生JS实现Ajax通过POST方式与PHP进行交互的方法示

通过POST方式与PHP进行交互var xmlHttp; //定义XMLHttpRequest对象function createXmlHttpRequestObject(){if(window.ActiveXObject){ //如果在internet Explorer下运行try{xmlHttpnew ActiveXObject("Microsoft.XMLHTTP");}catch(e){xmlHttpfalse;}}else{try{ //如…

linux系统在pe下查看ip地址,pe下查看原系统ip的方法_网站服务器运行维护

linux查看php环境是否安装_网站服务器运行维护linux查看php环境是否安装的方法&#xff1a;1、执行【find / -name php.ini】命令&#xff0c;查看系统是否有php的配置文件&#xff1b;2、执行【netstat -ntpl】命令&#xff0c;查看9000端口是否在运行。pe下查看原系统ip的方法…

【转发】淘宝网采用什么技术架构来实现网站高负载的

淘宝网采用什么技术架构来实现网站高负载的 2012-11-15 12:30 佚名 转载字号&#xff1a;T|T下面就结合淘宝目前的一些底层技术框架以及自己的一些感触来说说如何构建一个可 伸缩&#xff0c;高性能&#xff0c;高可用性的分布式互联网应用。 AD&#xff1a;2013云计算架构师峰…

新浪微博html5模板,个人主题建站首选微博秀模板,仿新浪微博官网

很久之前就想弄这个仿微博的模板了&#xff0c;但是时间一直不允许&#xff0c;这不抽空弄出来了&#xff0c;主题简单明了&#xff0c;后台设置简单&#xff0c;无需复杂操作&#xff0c;比起以往的CMS主题要简单的多&#xff0c;太适合做个人博客的网站了&#xff0c;当然这不…

【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器(转)

出处:http://edisonchou.cnblogs.com/ 一、反向代理&#xff1a;Web服务器的“经纪人” 1.1 反向代理初印象 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受internet上的连接请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;并将从…

大型网站系统架构分析大型网站系统架构分析

千万级的注册用户&#xff0c;千万级的帖子&#xff0c;nTB级的附件&#xff0c;还有巨大的日访问量&#xff0c;大型网站采用什么系统架构保证性能和稳定性&#xff1f; 首先讨论一下大型网站需要注意和考虑的问题。 数据库海量数据处理&#xff1a;负载量不大的情况下select、…

换服务器影响网站排名,换服务器对网站排名有影响吗?

很多站长朋友也许都会碰到这个问题&#xff0c;由于网站要备案从而要把数据搬移到其他服务器上去&#xff0c;都会先在网上搜索一下相关的信息&#xff0c;怕更换过后的服务器IP发生了更改&#xff0c;会影响百度蜘蛛的对网站的爬行&#xff0c;继而影响网站的收录和排名&#…

让你的网站免费支持 HTTPS 及 Nginx 平滑升级

为什么要使用 HTTPS ? 首先来说一下 HTTP 与 HTTPS 协议的区别吧&#xff0c;他们的根本区别就是 HTTPS 在 HTTP 协议的基础上加入了 SSL 层&#xff0c;在传输层对网络连接进行加密。简单点说在 HTTP 协议下你的网站是光着身子在奔跑&#xff0c;但到了 HTTPS 下你穿了一件衣…

PHP网站漏洞poc,ThinkPHP-5.0.23新的RCE漏洞测试和POC

TP5新RCE漏洞昨天又是周五&#xff0c;讨厌周五曝漏洞&#xff0c;还得又得加班&#xff0c;算了&#xff0c;还是先验证一波。新的TP5RCE&#xff0c;据说发现者因为上次的RCE&#xff0c;于是又审计了代码&#xff0c;结果发现的。TP5也成了万人轮啊。测试环境搭建windows下p…

商城网站优化

在一次正常的活动促销之后&#xff0c;客服开始陆续反馈有用户反应在抢标的时候打不开网页或者APP&#xff0c;在打开的时候标的就已经被抢光了&#xff0c;刚开始没有特别的上心&#xff0c;觉得抢标不就是这样吗&#xff0c;抢小米手机的时候也不就这样吗&#xff1f;随着活动…