Tăng hiệu suất ASP.NET Core với nội dung tĩnh

Tôi là một người ủng hộ các trình tạo trang web tĩnh. Chúng cung cấp sự kết hợp cuối cùng của hiệu suất cao và các tùy chọn triển khai, thời gian.

Đối với các nhà phát triển ASP.NET, chúng ta có quyền truy cập vào các công cụ xây dựng ứng dụng mạnh mẽ như Razor và ASP.NET Core. Cuối cùng thì tất cả các yếu tố động này kết hợp với nhau để tạo ra HTML.

Điều gì sẽ xảy ra nếu chúng ta có thể kết hợp tính năng động của ASP.NET Core với hiệu suất mạnh mẽ của các trang web tĩnh?

Chà, đây là tất cả những gì bài viết này nói về! Chúng ta sẽ xem cách chúng ta có thể sử dụng môi trường lưu trữ của ASP.NET Core để tạo các trang tĩnh từ nội dung động của chúng ta.

Điều kiện tiên quyết của ASP.NET Core

Khi làm việc với các ứng dụng web ASP.NET Core, chúng ta có thể sẽ xử lý một biến thể hỗ trợ Razor: ASP.NET Core MVC, Razor Pages hoặc Blazor. Trong bài viết này, chúng ta sẽ tập trung vào các công nghệ phía máy chủ MVC và Razor Pages, nhưng có thể lấy ví dụ này để làm việc với Blazor.

Chúng ta cũng sẽ cần đảm bảo rằng chúng ta cài đặt một số phần mềm trung gian (middleware) như một phần của đường ống (pipeline) ASP.NET Core của chúng ta:

  • DefaultFilesMiddleware
  • StaticFileMiddleware
  • RoutingMiddleware
  • SitemapMiddleware từ gói NuGet ParkSquare.AspNetCore.Sitemap. (không bắt buộc)

Hãy xem phương thức Configure trông như thế nào trong một ứng dụng ASP.NET Core MVC hoàn toàn mới sau:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    
    // important middlewares
    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseSitemap(new SitemapOptions());
    // end of important middleware
    
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

Xây dựng trình tạo HTML tĩnh

Một số yếu tố của ASP.NET Core MVC đang phát huy tác dụng của chúng để làm cho giải pháp này của chúng ta trở nên khả thi:

  • DefaultFileMiddleware cho phép chúng ta cung cấp các tệp index.html từ thư mục wwwroot một cách minh bạch.
  • StaticFileMiddleware được đăng ký trước khi bất kỳ thiết bị đầu cuối nào, cho phép máy chủ của chúng ta phục vụ các tập tin tĩnh trước khi chuyển đến các phương thức hành động trong MVC Controller của chúng ta.
  • SitemapMiddleware cung cấp cho chúng ta ý tưởng về tất cả các tuyến trong ứng dụng ASP.NET Core của chúng ta, mặc dù chúng ta có thể tạo bất kỳ tệp sitemap.xml nào thông qua các phương tiện khác hoặc thậm chí tự viết nó.
  • ASP.NET Core tạo ra các liên kết một cách tương đối, vì vậy các liên kết chỉ hoạt động.

Hãy đặt tất cả các yếu tố này lại với nhau và tạo ra một giải pháp!

Giải pháp tạo trang web tĩnh trong ASP.NET Core

Trong dự án ASP.NET Core MVC mới, chúng ta sẽ cần thêm hai gói NuGet: Oakton.AspNetCore ParkSquare.AspNetCore.Sitemap. Đây là tập tin csproj của tôi để tham khảo.

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="Oakton.AspNetCore" Version="3.0.0" />
      <PackageReference Include="ParkSquare.AspNetCore.Sitemap" Version="1.0.1" />
    </ItemGroup>

</Project>

Tiếp theo, chúng ta sẽ cần sửa đổi tệp Program để cho phép Oakton tìm thấy các lệnh từ dòng lệnh của chúng ta.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Oakton.AspNetCore;

[assembly:Oakton.OaktonCommandAssembly]

namespace StaticSiteTool
{
    public class Program
    {
        public static Task<int> Main(string[] args)
        {
            return CreateHostBuilder(args)
                .RunOaktonCommands(args);
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Cập nhật này của lớp Program sẽ tìm thấy tất cả các lệnh Oakton trong dự án của chúng ta và cho phép chúng ta chạy ứng dụng web của chúng ta hoặc một lệnh cụ thể. Hãy tạo class StaticPagesCommand ngay bây giờ.

using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Oakton;
using Oakton.AspNetCore;

namespace StaticSiteTool.Commands
{
    public class StaticPagesInput : NetCoreInput
    {
        [FlagAlias("url", 'u')]
        public string UrlFlag { get; set; }
            = "http://localhost:5000";

        [FlagAlias("sitemap", 's')]
        public string SitemapFlag { get; set; }
            = "sitemap.xml";
    }

    public class StaticPagesCommand : OaktonAsyncCommand<StaticPagesInput>
    {
        public override async Task<bool> Execute(StaticPagesInput input)
        {
            using var buildHost = input.BuildHost();

            var lifetime = buildHost
                .Services
                .GetRequiredService<IHostApplicationLifetime>();

            // process HTML files after the server 
            // has started up
            lifetime.ApplicationStarted.Register(async (state) =>
            {
                var host = (IHost) state;
                var webHostEnvironment = host
                    .Services
                    .GetRequiredService<IWebHostEnvironment>();

                var logger = host
                    .Services
                    .GetRequiredService<ILogger<StaticPagesCommand>>();

                logger.LogInformation($"Attempting to access {input.UrlFlag }.");

                var client = new HttpClient
                {
                    BaseAddress = new Uri(input.UrlFlag )
                };

                var siteMapResponse =
                    await client.GetAsync(input.SitemapFlag);

                var siteMap = await siteMapResponse.Content.ReadAsStringAsync();
                var xml = new XmlDocument();

                // load sitemap
                xml.LoadXml(siteMap);

                var locations = xml
                    .GetElementsByTagName("loc")
                    .Cast<XmlElement>();

                var wwwRoot = webHostEnvironment.WebRootPath;

                foreach (var location in locations)
                {
                    var uri = new Uri(location.InnerText);
                    var localPath = uri.LocalPath;

                    // write html to disk
                    if (Path.GetExtension(localPath) is "" or null)
                    {
                        localPath = Path.Combine(localPath, "index.html");
                    }

                    localPath = wwwRoot + localPath;

                    // delete the file so it doesn't
                    // get served instead of our endpoint
                    if (File.Exists(localPath))
                    {
                        File.Delete(localPath);
                    }

                    var page = await client.GetStringAsync(uri);
                    var directory = Directory.GetParent(localPath);

                    if (!directory.Exists)
                    {
                        directory.Create();
                    }

                    await File.WriteAllTextAsync(localPath, page);
                }
                
                await host.StopAsync();
                
            }, buildHost);

            await buildHost.RunAsync();

            return true;
        }
    }
}

Lệnh này sẽ khởi động máy chủ web của chúng ta và bắt đầu thực hiện các yêu cầu tới tất cả các điểm cuối ASP.NET Core được tìm thấy trong sitemap.xml của chúng ta. Chúng ta cũng muốn xóa mọi tệp hiện có, vì vậy máy chủ không gửi tệp tĩnh trong các lần chạy lệnh tiếp theo của chúng ta.

Để chạy lệnh, chúng ta có thể sử dụng các đối số dòng lệnh sau.

dotnet run staticpages

Công cụ này sẽ tạo các trang HTML tĩnh của chúng ta, chúng ta có thể thấy các trang này trong Trình khám phá Giải pháp trong hình ảnh trước và sau này.

Giải pháp tạo trang web tĩnh trong ASP.NET Core

Lần tới khi chúng ta chạy ứng dụng của mình, chúng ta sẽ thấy Kestrel phục vụ các tệp HTML tĩnh từ thư mục wwwroot. Không có kết xuất phía máy chủ hoặc mã động.

Giải pháp tạo trang web tĩnh trong ASP.NET Core

Phần kết luận

ASP.NET Core đủ linh hoạt để tạo một số trang web tĩnh tuyệt vời tại thời điểm biên dịch từ dòng lệnh. Kết hợp kỹ thuật này với một hệ thống xây dựng như GitHub Actions cũng có thể cho phép chúng ta xây dựng và triển khai ứng dụng ASP.NET Core cho một máy chủ tĩnh như GitHub Pages.

Hiệu quả của kỹ thuật này sẽ khác nhau tùy thuộc vào sự phụ thuộc và cấu hình của ứng dụng web của chúng ta, nhưng với ASP.NET Core, điều đó chưa bao giờ khả thi hơn thế.

Tôi hy vọng bạn thích bài viết này, và như mọi khi, cảm ơn bạn đã đọc.

Nếu Comdy hữu ích và giúp bạn tiết kiệm thời gian làm việc

Bạn có thể vui lòng đưa Comdy vào whitelist của trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi trong việc trả tiền cho dịch vụ lưu trữ web để duy trì hoạt động của trang web.

ASP.NET Core MVCASP.NET Core.NET CoreLập Trình C#
Bài Viết Liên Quan:
Tạo liên kết đến các endpoint của ASP.NET Core
Trung Nguyen 02/11/2021
Tạo liên kết đến các endpoint của ASP.NET Core

Trong bài viết này, chúng ta sẽ tìm hiểu cách đặt tên cho các endpoint để có thể tạo liên kết đến chúng từ bất kỳ đâu trong ứng dụng ASP.NET Core.

Cách thêm model vào dự án ASP.NET Core
Trung Nguyen 31/10/2021
Cách thêm model vào dự án ASP.NET Core

Trong bài viết này, chúng ta sẽ khám phá Model trong ASP.NET Core và các sub-framework khác như ASP.NET Core MVC, Razor Pages, Endpoints và Blazor.

Sử dụng Svelte trong dự án ASP.NET Core
Trung Nguyen 30/10/2021
Sử dụng Svelte trong dự án ASP.NET Core

Bài viết này sẽ giới thiệu cho bạn những điều cơ bản về việc thiết lập một dự án ASP.NET Core để sử dụng các component Svelte phía máy khách.

Background Service trong ASP.NET Core
Trung Nguyen 28/10/2021
Background Service trong ASP.NET Core

Bài viết này hướng dẫn cách triển khai một background service trong ứng dụng ASP.NET Core và cách giao tiếp với background service từ một HTTP request.