English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
В крупных веб-системах, чтобы повысить производительность системы при доступе, часто публикуют некоторые содержимое, которое не часто изменяется, в виде статических страниц, например, страницы с деталями продуктов в магазине, страницы с новостями. Эти данные, как только они будут опубликованы, частота изменений не будет высокой, если продолжать использовать динамический способ обработки, это肯定会 привести к значительным ресурсам сервера. Но мы не можем создавать статические страницы для каждого такого содержимого. Поэтому мы можем использовать метод псевдостатического обработки в системе, что такое псевдостатическое обработка, можно найти в Интернете. Мы здесь介绍一下, как реализовать псевдостатическое обработку в ASP.NET Core MVC.
В MVC-рамке view представляет собой вид, результат выполнения которого выводится в браузер клиента, включая html, css, js и т.д. Если мы хотим реализовать статическую страницу, нам нужно сохранить результат выполнения view в статический файл, сохраненный в указанном месте, например, на диске, в распределенном кеше и т.д., при следующем посещении можно напрямую читать сохраненное содержимое, не выполняя бизнес-логику снова. Как это сделать в ASP.NET Core MVC? Ответ - использовать фильтр. В MVC-рамке предоставляется несколько типов фильтров, и здесь мы используем фильтр действия, который предоставляет два момента времени: до выполнения действия и после выполнения действия. Мы можем проверить, была ли создана статическая страница до выполнения действия, если да, то напрямую читать содержимое файла и выводить его, пропуская дальнейшую логику. Если нет, продолжать往下, в момент после выполнения действия захватывать результат и сохранять созданный статический контент.
Тогда давайте конкретно реализуем код, сначала мы определяем тип фильтра, который мы называем StaticFileHandlerFilterAttribute, этот класс наследуется от ActionFilterAttribute, предоставляемого рамкой, StaticFileHandlerFilterAttribute перезаписывает два метода, предоставленных базовым классом: OnActionExecuted (после выполнения действия) и OnActionExecuting (до выполнения действия), конкретный код следующий:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class StaticFileHandlerFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext context){} public override void OnActionExecuting(ActionExecutingContext context){} }
В методе OnActionExecuting нужно проверить, уже ли сгенерировано статическое содержимое, если уже сгенерировано, напрямую выводим содержимое, логическое выполнение следующим образом
//Генерация имени статического файла по определенным правилам, здесь это area+"-"+controller+"-"+action+key string controllerName = context.RouteData.Values["controller"].ToString().ToLower(); string actionName = context.RouteData.Values["action"].ToString().ToLower(); string area = context.RouteData.Values["area"].ToString().ToLower(); //Здесь ключ по умолчанию равен id, конечно, мы можем настроить имя ключа differently 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", area, controllerName + "-" + actionName + (string.IsNullOrEmpty(id) ? "" : ("-" + id)) + ".html"); //Проверка существования файла if (File.Exists(filePath)) { //Если файл существует, напрямую читаем файл 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; } } }
В методе OnActionExecuted нам нужен результат действия, нужно проверить тип результата действия, является ли он ViewResult, если да, то выполнить этот результат кодом, получить результат вывода, создать статическую страницу по тем же правилам, конкретное выполнение следующим образом
//Получение результата IActionResult actionResult = context.Result; //Проверка, является ли результат ViewResult if (actionResult is ViewResult) { ViewResult viewResult = actionResult as ViewResult; //Следующий код выполняет этот ViewResult и помещает содержимое html в объект StringBuilder 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(); //这句一定要调用,否则内容就会是空的 //这句一定要调用,否则内容就会是空的 } //按照规则生成静态文件名称 string area = context.RouteData.Values["area"].ToString().ToLower(); 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", area); if (!Directory.Exists(devicedir)) { Directory.CreateDirectory(devicedir); } //Запись в файл string filePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", area, 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; }
Названный выше ключ, мы напрямую добавляем соответствующий атрибут
public string Key { get;set; }
Таким образом, мы можем использовать этот фильтр.Способ использования: добавьте атрибут [StaticFileHandlerFilter] к контроллеру или методу контроллера.Если нужно настроить другой ключ, можно использовать [StaticFileHandlerFilter(Key="установленное значение")]
Статичное создание уже реализовано, нам также нужно подумать о обновлениях. Если контент статьи обновлен на后台, нам нужно также обновить статическую страницу. Есть много решений: одно из них - при обновлении контента на后台同步删除对应的静态页。Мы здесь介绍一下 другой метод - регулярное обновление, это дать статическим страницам определенный срок действия, после истечения которого они автоматически обновляются.Для реализации этой логики нам нужно получить время создания статической страницы в методе OnActionExecuting, затем сравнить его с текущим временем и判断是否已经过期. Если не истек срок действия, напрямую выводить контент, если истек, продолжить выполнение последующих логик.Конкретный код выглядит следующим образом:
//获取文件信息对象 FileInfo fileInfo=new FileInfo(filePath); //结算时间间隔,如果小于等于两分钟,就直接输出,当然这里的规则可以改 TimeSpan ts = DateTime.Now - fileInfo.CreationTime; if(ts.TotalMinutes<=2) { using (FileStream fs = File.Open(filePath, FileMode.Open)) { using (StreamReader sr = new StreamReader(fs, Encoding.UTF8)) { ContentResult contentresult = new ContentResult(); contentresult.Content = sr.ReadToEnd(); contentresult.ContentType = "text/html"; context.Result = contentresult; } } }
Теперь伪静态已经实现了。Текущий метод может в некоторой степени повысить производительность доступа, но для крупных портальных систем это может быть远远不够. Следуя способу,介绍的 выше, можно расширить другие функции, например, после создания статических страниц их можно发布 на CDN, а также на отдельный сервер содержимого и т.д. Независимо от того, какой способ используется,思路都是一样的。
Вот и все, что было в этой статье, я надеюсь, что это поможет вам в вашем обучении, и希望大家多多支持呐喊教程。
Объявление: содержимое статьи взято из Интернета, авторские права принадлежат соответствующему владельцу, контент предоставлен пользователями Интернета, сайт не обладает правами собственности, не undergone редактирование, и не несет ответственности за соответствующие юридические последствия. Если вы обнаружите контент,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма, пожалуйста, замените # на @) для сообщения о нарушении, и предоставьте соответствующие доказательства. Как только будет установлено, что информация верна, сайт немедленно удалят涉嫌侵权的内容。