# ASP.NET博客能有多简单?

<!--category-- ASP.NET, Markdown, Blogging -->
<datetime class="hidden">2025-12-01T12:00</datetime>

## 一. 导言 导言 导言 导言 导言 导言 一,导言 导言 导言 导言 导言 导言

如果你一直关注这个博客, 你可能会注意到我的主要博客平台是... 让我们把它称为“热心工程”。 PostgreSQL 和矢量数据库,语义和全文搜索 GIN 索引, 自动翻译成14种语言, 多个主机服务, 挂机工作时间安排, Prometheus 度量仪, Serilog 跟踪, HTMX 互动, 使用我自己的纽扣包, 以及足够多的多克容器 使船嫉妒。

**这完全是故意的** 这个网站是我的活实验室 一个操场 我在那里实验技术 测试部署策略 测量性能特征 *假定* 被过度设计,因为这就是我学习的方法: 通过解决大多数博客实际上并不存在的问题, 然后把这些解决方案包装成开放源码图书馆, 其他人也可以使用。

但事情是这样的: **你可能不需要 任何这些来运行一个博客。**

这就是为什么我创造了 **多数是混杂的。 最小的Blog** 没有数据库,没有管道,没有复杂。只要在文件夹中标记文件,在网络上出现。这是博客在不将其作为实验室时的样子。

> 注:关于与来源的链接,见本条结尾部分,我计划将此作为附件1予以公布。 [核金包包](https://www.nuget.org/packages?q=mostlylucid&includeComputedFrameworks=true&prerel=true&sortby=relevance) 一旦我得到时间确保它百分之百可靠, 而且它不是太可怕(所以尽快寻找 K6 测试物品! ) 。

[TOC]

## 哲学:少就是少

整个项目是围绕一项原则设计的: **保持简单**没有数据库,没有建设管道,没有 JavaScript 框架,只有 ASP.NET 9.0, 马克迪 用于加分分析, 还有大约500行代码总计。
注意:你甚至可以使用 [标记自下调](https://github.com/markdown-it/markdown-it) 然后只有服务器站点地图静态 `.md` 这个博客是 ASP. NET 博客(kinda sorta {。

## 项目结构

让我们看看这个项目是如何组织的:

```
Mostlylucid.MinimalBlog/
├── Pages/
│   ├── Index.cshtml              # Homepage with post list
│   ├── Post.cshtml                # Individual post page
│   ├── Categories.cshtml          # List of all categories
│   ├── Category.cshtml            # Posts in a category
│   ├── _Layout.cshtml             # Shared layout
│   ├── _ViewImports.cshtml        # Shared imports
│   └── _ViewStart.cshtml          # Layout selection
├── wwwroot/
│   └── css/
│       └── site.css               # All the CSS you need
├── MarkdownBlogService.cs         # Core blog logic
├── MetaWeblogService.cs           # XML-RPC for external editors
├── Program.cs                     # Application setup
├── appsettings.json               # Configuration
└── Mostlylucid.MinimalBlog.csproj # Project file
```

## 心脏:马克下布劳作服务

博客的核心是 `MarkdownBlogService` 类。非常简单,只是120行代码 处理:

1. 从目录读取标记下载文件
2. 分析元数据(标题、类别、公布日期)
3. 使用 Markdig 将记分转换为 HTML
4. 紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧紧记

以下是它是如何工作的:

### 加载员额

服务扫描已配置的目录 `.md` 并把它们全部装入内存 :

```csharp
private List<BlogPost> LoadAllPosts()
{
    if (!Directory.Exists(_markdownPath)) return [];

    return Directory.GetFiles(_markdownPath, "*.md", SearchOption.TopDirectoryOnly)
        .Where(f => Path.GetFileName(f).Count(c => c == '.') == 1) // Only base .md files
        .Select(ParseFile)
        .Where(p => p is { IsHidden: false })
        .OrderByDescending(p => p!.PublishedDate)
        .ToList()!;
}
```

注意聪明的过滤: `Count(c => c == '.') == 1` 确保我们只有基础 `.md` 文件, 不是翻译版本的 `post.ar.md` 或 `post.de.md` (如果您想稍后添加译文)

### 解析元元数据

每个标记文件遵循简单的常规 :

```markdown
# Post Title

<!-- category -- Category1, Category2 -->
<datetime class="hidden">2024-11-30T12:00</datetime>

Your content here...
```

解析器使用正则表达式和 Markdig AST 提取此元数据 :

```csharp
private BlogPost? ParseFile(string filePath)
{
    var markdown = File.ReadAllText(filePath);
    var slug = Path.GetFileNameWithoutExtension(filePath);
    var document = Markdown.Parse(markdown, _pipeline);

    // Extract title from first H1
    var title = document.Descendants<HeadingBlock>()
        .FirstOrDefault(h => h.Level == 1)?
        .Inline?.FirstChild?.ToString() ?? slug;

    // Extract categories: <!-- category -- Cat1, Cat2 -->
    var categoryMatch = CategoryRegex().Match(markdown);
    var categories = categoryMatch.Success
        ? categoryMatch.Groups[1].Value.Split(',', StringSplitOptions.TrimEntries)
        : [];

    // Extract date: <datetime class="hidden">2024-01-01T00:00</datetime>
    var dateMatch = DateTimeRegex().Match(markdown);
    var publishedDate = dateMatch.Success && DateTime.TryParse(dateMatch.Groups[1].Value, out var dt)
        ? dt : File.GetCreationTimeUtc(filePath);

    return new BlogPost
    {
        Slug = slug,
        Title = title,
        Categories = categories,
        PublishedDate = publishedDate,
        HtmlContent = Markdown.ToHtml(markdown, _pipeline),
        IsHidden = markdown.Contains("<hidden")
    };
}
```

### 缓结战略

服务使用的每一种方法 `IMemoryCache` 以避免在每次请求时重读和重新标注文件:

```csharp
public IReadOnlyList<BlogPost> GetAllPosts()
{
    return cache.GetOrCreate("all_posts", entry =>
    {
        entry.SetOptions(CacheOptions);
        return LoadAllPosts();
    }) ?? [];
}
```

缓存条目有30分钟的滑动过期和2小时的绝对过期。 简单、 有效 。

## 应用程序设置: 程序. cs

整个应用程序设置只有43行: Razor Pages, 内存缓存, 输出缓存, 两个单通服务, 静态文件服务, 和一个 MetaWeblog XML- RPC 端点。 全部缓存为单通, 因为除非文件被修改, 否则不会发生任何变化 。

## UI:简单剃刀页面

用户界面是纯服务器发送的 HTML 。 No JavaScript, no HTMX, no Alpine.js。 主页列表、 邮页 `@Html.Raw(post.HtmlContent)` 具有 a 和 `[OutputCache]` 小时 HTML 缓存属性。共四页,每行30行以下。

## 定型: CSS 55 行

整个视觉设计由仅55行的单个 CSS 文件处理。 它使用 CSS 定制的特性处理这些设计, 并创建了干净、 黑暗的 GitHub 启发的外观 :

```css
:root {
  --bg: #0d1117;
  --bg-card: #161b22;
  --text: #c9d1d9;
  --text-muted: #8b949e;
  --accent: #58a6ff;
  --border: #30363d;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  max-width: 48rem;
  margin: 0 auto;
  padding: 2rem 1rem;
}

/* ... more styles ... */
```

没有预处理器,没有建筑步骤,没有数千个通用课程,只有干净的、可读的、有效的CSS。

## 慈善特写:MetaWeblog API

对于那些喜欢专门减记编辑的作家来说 [Markdown 怪物](https://markdownmonster.west-wind.com/)XML-RPC API允许外部编辑:

- 名单员额
- 创建新职位
- 编辑现有员额
- 删除员额
- 上传图像
- 检索分类

正在执行 `MetaWeblogService.cs` 并处理完整的 XML- RPC 协议, 解析请求和生成回复。 这意味着您可以在您最喜爱的编辑中写入您的博客文章, 并直接发布到您的博客中 。

## 配置配置配置

整个配置文件只有 14 行 :

```json
{
  "MarkdownPath": "../Mostlylucid/Markdown",
  "ImagesPath": "wwwroot/images",
  "MetaWeblog": {
    "Username": "admin",
    "Password": "changeme",
    "BlogUrl": "http://localhost:5000"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}
```

- `MarkdownPath` - 你的标记文件在哪里运行
- `ImagesPath` - 存放图像
- `MetaWeblog` - 外部编辑访问证书

## 用作 NuGet 软件包

> 如上所述,它将提供但尚不能提供:)

这个博客现在以NuGet 软件包的形式提供,

```bash
dotnet add package mostlylucid.MinimalBlog
```

然后在你的 `Program.cs`:

```csharp
builder.Services.AddRazorPages();
builder.Services.AddMinimalBlog(options =>
{
    options.MarkdownPath = "Markdown";
    options.ImagesPath = "wwwroot/images";
    options.EnableMetaWeblog = false; // Optional, defaults to true
});

var app = builder.Build();

app.UseStaticFiles();
app.UseMinimalBlog();
app.MapRazorPages();
app.Run();
```

仅用两种方法调用(`AddMinimalBlog` 和 `UseMinimalBlog`你有一个工作博客。

## 运行样本工程

管理包括样本项目:

```bash
cd Mostlylucid.MinimalBlog
dotnet run
```

访问访问访问访问 `http://localhost:5000` 你会看到这个博客 从配置路径上加了标记文件。

## 创建内容

创建新博客文章:

1. 创建新 `.md` 在您配置的文件中 `MarkdownPath`
2. 添加标准元数据 :
   ```markdown
   # Your Post Title
   
   <!-- category -- YourCategory, AnotherCategory -->
   <datetime class="hidden">2024-11-30T12:00</datetime>
   
   Your content here...
   ```
3. 保存文件
4. 缓存将在30分钟内过期( 或重新启动应用程序)

要添加图像,只需将其放在您的配置中 `ImagesPath` 并在您的标记中引用目录 :

```markdown
![Alt text](your-image.jpg)
```

## 缺少什么(目的)

这个最小的博客故意不包括:

- **评论评论评论评论评论评论评论评论评论评论评论评论评论评论评论评论评论评论评论评论** - 必要时使用第三方服务
- **搜索搜索** - 将内容按类别排列
- **标记标记** - 对小博客来说,分类就足够了
- **RSS/阿通** - 如果需要,简单添加
- **验证** - MetaWeblog API只使用基本认证
- **分析分析** - 如果需要,添加 JavaScript 片段
- **SEO优化** - 使用基本元标签很好工作
- **响应图像** - 浏览器处理
- **黑暗/灯光主题切换** -一个主题就足够了

这些特征都是 *可能(可能)* 添加,但是它们不是默认包含的, 因为大多数小博客都不需要它们。

## 性能特点

尽管简简单简洁, **快速快速**:

- **内存缓存** 表示第一次装入后没有文件 I/O
- **输出缓存** 表示在第一次请求后,没有 Razor 显示
- **无数据库** 意指不询问间接费用
- **无 JavaScript 无 JavaScript** 表示快速的页面负载
- **简单 CSS** 表示最小样式表解析

对于一个中小博客(不到1000个文章)来说,

## 何时使用此博客与全部最精美博客的对比

使用使用 **最优精华。 最小Blog** 时间:

- 你开始一个私人博客
- 你的职位不足500个
- 你不需要多种语言
- 你想把事情简单化
- 你对标记文件很自在
- 你只想写和出版

使用 **完全最湿润的平台平台** 时间:

- 你用你的博客作为 **学习实验室** 应用新的技术促进新的技术
- 您想要尝试部署策略, 监控, 和业绩优化
- 您需要特定的特点, 如多语言支持、全文搜索或评论
- 你正在建造包包 需要一个真实世界的测试床
- 你在记录复杂的技术实施
- 建造平台的旅程与平台所容纳的内容一样宝贵

## 结论:作为地物的简单性

在现代网络开发世界中,我们常常默认地寻求复杂的解决方案。 需要一个博客吗? 更好地建立数据库,配置ORM, 设置迁移, 添加缓存, 执行搜索, 配置背景工作...

但有时简单的解决办法是 *右右右右右* MinimalBlog 证明您可以建立一个功能性、快速和可维护的博客平台, 包括:

- **C# 342行** ( MarkdownBlog Service + MetaWeblogService + 程序.cs)
- **~ 120 条Razor 标记线** (4页)
- **CSS 55行**
- **NuGet 依赖性 1NuGet** (马克迪)

那是 **不到520行总代码** 一个完整的博客平台。

此工程既是一个功能性博客平台,又是一个提醒: 在您添加复杂性之前, 请先问问自己是否真的需要它。 有时您需要的只是一个包含标记文件的文件夹 。

您可以在 The 中找到完整的源代码 [最优精细化。 最小Blog 目录](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.MinimalBlog) 我对密码一满意我就把金币包放出来

博客快乐!