为了缓解服务器的压力,我们可以将一些不经常更新的页面生成html页面,然后保存到服务器,页面进行查询时,我们先查找html是否存在,如果存在,我们直接返回html页面,否则就生成对应的html页面。这里我们通过动作过滤器ActionFilterAttribute实现。
定义属性
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace xiangcaowuyu.net.Public.Attribute
{
/// <summary>
/// 实现伪静态
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class StaticFileHandlerFilterAttribute : ActionFilterAttribute
{
public string Key
{
get; set;
}
/// <summary>
/// 动作执行后
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuted(ActionExecutedContext context)
{
//获取结果
IActionResult actionResult = context.Result;
//判断结果是否是一个ViewResult
if (actionResult is ViewResult)
{
ViewResult viewResult = actionResult as ViewResult;
//下面的代码就是执行这个ViewResult,并把结果的html内容放到一个StringBuiler对象中
var services = context.HttpContext.RequestServices;
var executor = services.GetRequiredService<ViewResultExecutor>();
var option = services.GetRequiredService<IOptions<MvcViewOptions>>();
var result = executor.FindView(context, viewResult);
result.EnsureSuccessful(originalLocations: null);
var view = result.View;
StringBuilder builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
var viewContext = new ViewContext(
context,
view,
viewResult.ViewData,
viewResult.TempData,
writer,
option.Value.HtmlHelperOptions);
view.RenderAsync(viewContext).GetAwaiter().GetResult();
//这句一定要调用,否则内容就会是空的
writer.Flush();
}
//按照规则生成静态文件名称
string controllerName = context.RouteData.Values["controller"].ToString().ToLower();
string actionName = context.RouteData.Values["action"].ToString().ToLower();
string id = context.RouteData.Values.ContainsKey(Key) ? context.RouteData.Values[Key].ToString() : "";
if (string.IsNullOrEmpty(id) && context.HttpContext.Request.Query.ContainsKey(Key))
{
id = context.HttpContext.Request.Query[Key];
}
string devicedir = Path.Combine(AppContext.BaseDirectory, "wwwroot");
if (!Directory.Exists(devicedir))
{
Directory.CreateDirectory(devicedir);
}
//写入文件
string filePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", controllerName + "-" + actionName + (string.IsNullOrEmpty(id) ? "" : ("-" + id)) + ".html");
using (FileStream fs = File.Open(filePath, FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
{
sw.Write(builder.ToString());
}
}
//输出当前的结果
ContentResult contentresult = new ContentResult();
contentresult.Content = builder.ToString();
contentresult.ContentType = "text/html";
context.Result = contentresult;
}
}
/// <summary>
/// 动作执行前
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext context)
{
//按照一定的规则生成静态文件的名称,这里是按照area+"-"+controller+"-"+action+key规则生成
string controllerName = context.RouteData.Values["controller"].ToString().ToLower();
string actionName = context.RouteData.Values["action"].ToString().ToLower();
//这里的Key默认等于id,当然我们可以配置不同的Key名称
string id = context.RouteData.Values.ContainsKey(Key) ? context.RouteData.Values[Key].ToString() : "";
if (string.IsNullOrEmpty(id) && context.HttpContext.Request.Query.ContainsKey(Key))
{
id = context.HttpContext.Request.Query[Key];
}
string filePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", controllerName + "-" + actionName + (string.IsNullOrEmpty(id) ? "" : ("-" + id)) + ".html");
//判断文件是否存在
if (File.Exists(filePath))
{
FileInfo fileInfo = new FileInfo(filePath);
TimeSpan timeSpan = DateTime.Now - fileInfo.CreationTime;
if (timeSpan.TotalDays <= 1)
{
//如果存在,直接读取文件
using (FileStream fs = File.Open(filePath, FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs, Encoding.UTF8))
{
//通过contentresult返回文件内容
ContentResult contentresult = new ContentResult();
contentresult.Content = sr.ReadToEnd();
contentresult.ContentType = "text/html";
context.Result = contentresult;
}
}
}
}
}
}
}
使用
我们可以通过将属性设置到类或者方法上面来使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using xiangcaowuyu.net.Models.Entity;
using xiangcaowuyu.net.Public.ArticleHelper;
using xiangcaowuyu.net.Public.MenuHelper;
using Microsoft.AspNetCore.Authorization;
using xiangcaowuyu.net.Public.Attribute;
namespace xiangcaowuyu.net.Controllers
{
[StaticFileHandlerFilter(Key = "id")]
public class ArticleController : BaseController
{
private readonly IArticleHelper articleHelper;
public ArticleController(IArticleHelper articleHelper, IMenuHelper menuHelper) : base(menuHelper)
{
this.articleHelper = articleHelper;
}
// GET: Article
public ActionResult Index()
{
List<Article> list = articleHelper.GetList();
return View(list);
}
// GET: Article/Details/5
[StaticFileHandlerFilter(Key = "id")]
public ActionResult Details(string id)
{
Article article = articleHelper.GetArticle(id);
return View(article);
}
// GET: Article/Create
[Authorize(Roles = "admin")]
public ActionResult Create()
{
return View();
}
// POST: Article/Create
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "admin")]
public ActionResult Create(Article article)
{
try
{
articleHelper.InsertArticle(article);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: Article/Edit/5
[Authorize(Roles = "admin")]
public ActionResult Edit(string id)
{
Article article = articleHelper.GetArticle(id);
return View(article);
}
// POST: Article/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "admin")]
public ActionResult Edit(string id, Article article)
{
try
{
// TODO: Add update logic here
articleHelper.EditArticle(article);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: Article/Delete/5
[Authorize(Roles = "admin")]
public ActionResult Delete(string id)
{
if (articleHelper.DeleteArticle(id))
{
ViewBag.IsDeleted = "删除成功";
}
else
{
ViewBag.IsDeleted = "删除失败";
}
return View();
}
// POST: Article/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "admin")]
public ActionResult Delete(string id, IFormCollection collection)
{
try
{
// TODO: Add delete logic here
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
}
}
查看结果
我们查看wwroot页面,可以查看生成的html页面
哇,还要在要回复一次?
怎么看不了啊