AI Reconstruct Razor Pages Website Completed

AI Reconstruct Razor Pages Website Completed

From Blazor static SSR back to Razor Pages, source code interpretation of website architecture design and core implementation

Last updated 4/16/2026 11:00 PM
沙漠尽头的狼
6 min read
Category
.NET Frontend
Tags
.NET C# ASP.NET Core Blazor Razor Pages

The website has moved from Blazor back to Razor Pages. This article explains the current architecture of the website from a source code perspective.

1. Why switch from Blazor to Razor Pages

The front end of the website was previously developed using Blazor static SSR. After deeper consideration of content display websites, it was determined that Razor Pages might be more suitable for such scenarios.

2. Website Project Structure

Source Repository: CodeWF, using a front-end/back-end separation architecture:

CodeWF/src/
├── WebApp/                 # Front-end site (Razor Pages)
│   ├── Pages/             # Page files
│   ├── Components/        # View Components
│   ├── Controllers/      # API controllers
│   └── wwwroot/          # Static assets
│
└── CodeWF/                # Core class library
    ├── Models/           # Data models
    ├── Services/         # Business services
    └── Extensions/       # Extension methods

CodeWF Architecture

3. Razor Pages Core Implementation

3.1 Page Model

Using the tools page as an example, a typical PageModel implementation:

// Pages/Tool/Index.cshtml.cs
namespace WebApp.Pages.Tool;

public class IndexModel : PageModel
{
    private readonly AppService _appService;

    public List<ToolItem>? Tools { get; set; }

    public IndexModel(AppService appService)
    {
        _appService = appService;
    }

    public async Task OnGetAsync()
    {
        Tools = await _appService.GetAllToolItemsAsync();
    }
}

Corresponding view file Pages/Tool/Index.cshtml:

@page
@model WebApp.Pages.Tool.IndexModel
@{
    ViewData["Title"] = "Tools";
}

<div class="container">
    <h1 class="mb-4">Online Tools</h1>
    <div class="row">
        @foreach (var tool in Model.Tools)
        {
            <div class="col-md-4 mb-4">
                <div class="card shadow-sm h-100">
                    <div class="card-body">
                        <h4 class="card-title">@tool.Name</h4>
                        @if (!string.IsNullOrEmpty(tool.Memo))
                        {
                            <p class="card-text text-muted">@tool.Memo</p>
                        }
                        @if (tool.Children != null && tool.Children.Any())
                        {
                            <ul class="list-group list-group-flush mt-3">
                                @foreach (var child in tool.Children)
                                {
                                    <li class="list-group-item">
                                        <a href="@child.Slug">@child.Name</a>
                                    </li>
                                }
                            </ul>
                        }
                    </div>
                </div>
            </div>
        }
    </div>
</div>

3.2 Route Parameter Binding

The article detail page uses route template syntax:

@page "/{year:int}/{month:int}/{slug}"
@model WebApp.Pages.Blog.Post.IndexModel

Corresponding code-behind Post/Index.cshtml.cs:

public class IndexModel : PageModel
{
    public BlogPost? Post { get; set; }

    public async Task OnGetAsync(int year, int month, string slug)
    {
        Post = await _appService.GetPostBySlug(slug);
    }
}

3.3 Shared Layout

Layout file defining the global page structure:

@inject CodeWF.Services.AppService AppService
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - CodeWF</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-lg navbar-dark bg-tech-nav fixed-top">
            <!-- Navigation bar content -->
        </nav>
    </header>

    <div style="padding-top: 80px;">
        @RenderBody()
    </div>

    @await Component.InvokeAsync("FriendLink")

    <footer>
        <!-- Footer content -->
    </footer>
</body>
</html>

4. Service Layer Design

4.1 AppService Core Service

AppService.cs is the core service class for the entire application:

public class AppService(IOptions<SiteOption> siteOption)
{
    private List<BlogPost>? _blogPosts;
    private List<ToolItem>? _toolItems;
    private List<DocItem>? _docItems;
    private List<CategoryItem>? _categoryItems;
    private List<AlbumItem>? _albumItems;

    public async Task<List<BlogPost>?> GetAllBlogPostsAsync()
    {
        if (_blogPosts?.Any() == true) return _blogPosts;

        _blogPosts = [];
        var endYear = DateTime.Now.Year;

        for (var start = siteOption.Value.StartYear; start <= endYear; start++)
        {
            var postDir = Path.Combine(siteOption.Value.LocalAssetsDir, start.ToString());
            if (!Directory.Exists(postDir)) continue;

            var postFiles = Directory.GetFiles(postDir, "*.md", SearchOption.AllDirectories);
            foreach (var postFile in postFiles)
            {
                var blogPost = await ReadBlogPostAsync(postFile);
                if (!blogPost.Draft)
                {
                    _blogPosts.Add(blogPost);
                }
            }
        }

        _blogPosts = _blogPosts
            .OrderByDescending(post => post.Lastmod ?? post.Date ?? DateTime.MinValue)
            .ThenByDescending(post => post.Date ?? DateTime.MinValue)
            .ToList();
        return _blogPosts;
    }

    public async Task SeedAsync()
    {
        // Preload all data on startup
        await GetAllAlbumItemsAsync();
        await GetAllCategoryItemsAsync();
        await GetAllBlogPostsAsync();
        await GetAllFriendLinkItemsAsync();
        await GetAllDocItemsAsync();
        await GetAllToolItemsAsync();
    }
}

4.2 Data Models

// BlogPost.cs
public class BlogPostBrief
{
    public string? Title { get; set; }
    public string? Slug { get; set; }
    public string? Description { get; set; }
    public DateTime? Date { get; set; }
    public DateTime? Lastmod { get; set; }
    public string? Author { get; set; }
    public string? Cover { get; set; }
    public List<string>? Categories { get; set; }
    public List<string>? Tags { get; set; }
}

public class BlogPost : BlogPostBrief
{
    public string? Content { get; set; }      // Raw Markdown
    public string? HtmlContent { get; set; }  // Converted HTML
}

5. Page Request Processing Flow

Page Request Processing Flow

  1. User requests /timestamp
  2. Routing system matches @page "/timestamp"
  3. Executes OnGetAsync() method (if exists)
  4. Retrieves business data via AppService
  5. Renders view and returns HTML

6. Program.cs Configuration

var builder = WebApplication.CreateBuilder(args);

// Add Razor Pages services
builder.Services.AddRazorPages();
builder.Services.AddControllers();
builder.Services.AddHttpClient();

// Dependency inject AppService
builder.Services.AddSingleton<AppService>();
builder.Services.Configure<SiteOption>(builder.Configuration.GetSection("Site"));

var app = builder.Build();

// Preload data on startup
using (var serviceScope = app.Services.CreateScope())
{
    var service = serviceScope.ServiceProvider.GetRequiredService<AppService>();
    await service.SeedAsync();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();

app.Run();

7. Online Tool Example: Timestamp Conversion

Using Timestamp.cshtml to demonstrate front-end interaction:

@page "/timestamp"
@{ ViewData["Title"] = "Timestamp Converter Tool"; }

<div class="container">
    <h1 class="mb-4">Timestamp Converter Tool</h1>

    <div class="card mb-3">
        <div class="card-body">
            <h5 class="card-title">Timestamp → Date</h5>
            <div class="row mb-3">
                <div class="col d-flex align-items-center gap-2">
                    <span>Timestamp:</span>
                    <input type="text" class="form-control" style="width: 200px;"
                           id="inputTimestamp" placeholder="Enter timestamp" />
                    <select class="form-select" style="width: 100px;" id="timestampUnit">
                        <option value="s">Seconds</option>
                        <option value="ms">Milliseconds</option>
                    </select>
                    <button class="btn btn-primary btn-sm"
                            onclick="convertTimestampToDate()">Convert to Date</button>
                </div>
            </div>
        </div>
    </div>
</div>

@section Scripts {
    <script>
        function convertTimestampToDate() {
            const timestamp = document.getElementById('inputTimestamp').value;
            const unit = document.getElementById('timestampUnit').value;
            const ms = unit === 's' ? timestamp * 1000 : parseInt(timestamp);
            const date = new Date(ms);
            document.getElementById('outputDate').value = date.toLocaleString();
        }
    </script>
}

8. Summary

The source code is open source, and we welcome exchanges and learning.

Repository URL: https://github.com/dotnet9/CodeWF

Keep Exploring

Related Reading

More Articles
Same category / Same tag 3/10/2025

Parking Move QR Code Generation Tool Development Practice

This article introduces how to develop a parking move QR code generation tool, including a desktop version implemented with C# and Avalonia, as well as an online version implemented with Blazor frontend and .NET Web API, covering requirements analysis, core code implementation, UI design, and application of MVVM pattern.

Continue Reading
Same category / Same tag 6/20/2024

CodeWF.EventBus: Lightweight Event Bus for Smoother Communication

CodeWF.EventBus is a flexible event bus library that enables decoupled communication between modules. It supports various .NET project types such as WPF, WinForms, ASP.NET Core, etc. With a clean design, it easily implements command publishing and subscribing, as well as requests and responses. Through orderly event handling, it ensures events are properly processed. Simplify your code and improve system maintainability.

Continue Reading
Same category / Same tag 1/19/2024

FluentValidation Validation Tutorial Based on .NET

FluentValidation is a validation framework based on .NET development. It is open-source, free, and elegant, supporting chained operations, easy to understand, feature-complete, and can be deeply integrated with MVC5, WebApi2, and ASP.NET Core. The component provides over a dozen commonly used validators, good scalability, support for custom validators, and support for localization and multilingual.

Continue Reading