# MinimalBlog - Hoe eenvoudig kan een ASP.NET Blog Echt zijn?

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

## Inleiding

Als je deze blog hebt gevolgd, heb je misschien gemerkt dat mijn belangrijkste blogplatform is... Laten we het "enthousiastly engineered" noemen. PostgreSQL EN vector databases, semantisch EN full-text zoeken met GIN indexen, geautomatiseerde vertaling naar 14 talen, meerdere gehoste diensten, Hangfire job planning, Prometheus metrics, Serilog traceren, HTMX interacties, usign mijn eigen nuget pakketten, en genoeg Docker containers om een schip jaloers te maken.

**Dat is volledig opzettelijk.** Deze site is mijn living lab - een speeltuin waar ik experimenteer met technologieën, testen implementatie strategieën, het meten van de prestaties kenmerken, en het bouwen van herbruikbare pakketten. *verondersteld* Om te worden over-ontworpen omdat dat is hoe ik leer: door het oplossen van problemen die de meeste blogs niet hebben, dan het verpakken van die oplossingen als open-source bibliotheken kunnen anderen gebruiken.

Maar het zit zo: **Je hebt waarschijnlijk niets van dat nodig om een blog uit te voeren.**

Dat is waarom ik creëerde **meestal lucid.minimalBlog** - om te laten zien wat er gebeurt als je alle experimenten weghaalt en je focust op de absolute essentials. Geen database. Geen bouwpijplijn. Geen complexiteit. Gewoon bestanden markeren in een map die op het web verschijnt. Dit is hoe een blog eruit ziet als je het niet als laboratorium gebruikt.

> OPMERKING: Zie het einde van het artikel voor een link naar de bron, Ik ben van plan om dit vrij te geven als een [nuget-pakket](https://www.nuget.org/packages?q=mostlylucid&includeComputedFrameworks=true&prerel=true&sortby=relevance) Zodra ik tijd heb om ervoor te zorgen dat het 100% betrouwbaar is en het perf is niet TOO verschrikkelijk (dus zoek snel naar k6 testartikelen!).

[TOC]

## De filosofie: Minder is meer

Het hele project is ontworpen rond één principe: **Hou het simpel.**. Geen database, geen build pipeline, geen JavaScript framework. Gewoon ASP.NET 9.0, Markdig voor markdown parsing, en ongeveer 500 regels code totaal. Dat is het.
OPMERKING: U kunt zelfs doen deze client kant door gebruik te maken van de likes van [markdown-it](https://github.com/markdown-it/markdown-it) dan gewoon de server site kaart statische `.md` bestanden en maak het zelfs SIMPLER, maar... nou dit is een ASP.NET blog (een soort van).

## Projectstructuur

Laten we eens kijken naar hoe het project wordt georganiseerd:

```
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
```

## Het hart: MarkdownBlogService

De kern van de blog is de `MarkdownBlogService` Het is opmerkelijk eenvoudig... 120 regels code die hanteren:

1. Opmaakbestanden uit een map aan het lezen
2. Metadata ontleden (titel, categorieën, datum publiceren)
3. Markdown omzetten naar HTML met Markdig
4. Alles in het geheugen stoppen

Dit is hoe het werkt:

### Laden van berichten

De service scant een geconfigureerde map voor `.md` bestanden en laadt ze allemaal in het geheugen:

```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()!;
}
```

Let op de slimme filtering: `Count(c => c == '.') == 1` zorgt ervoor dat we alleen honk krijgen `.md` bestanden, niet vertaalde versies zoals `post.ar.md` of `post.de.md` (voor het geval u later vertalingen wilt toevoegen).

### Metadata ontleden

Elk markdown-bestand volgt een eenvoudige conventie:

```markdown
# Post Title

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

Your content here...
```

De parser haalt deze metadata uit met behulp van reguliere expressies en de 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")
    };
}
```

### Caching Strategie

Elke methode in de service gebruikt `IMemoryCache` om te voorkomen dat bestanden opnieuw worden gelezen en opnieuw worden verwerkt op elk verzoek:

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

Cache-inzendingen hebben een 30 minuten sliding expiration en 2 uur absolute vervaldatum. Eenvoudig, effectief.

## Application Setup: Programma.cs

De gehele applicatie setup is slechts 43 regels: Razor Pages, geheugen cache, output cache, twee singleton diensten, statische bestand dienen, en een MetaWeblog XML-RPC eindpunt. Alles gecached als singletons omdat er niets verandert tenzij bestanden worden gewijzigd.

## De UI: Simple Razor Pagina's

De UI is pure server-rendered HTML. Geen JavaScript, geen HTMX, geen Alpine.js. De homepage bevat berichten, de postpagina rendert `@Html.Raw(post.HtmlContent)` met een `[OutputCache]` attribuut voor urenlange HTML-caching. Vier pagina's totaal, elk onder 30 regels.

## Stijl: 55 lijnen van CSS

Het volledige visuele ontwerp wordt behandeld door een enkel CSS-bestand met slechts 55 lijnen. Het maakt gebruik van CSS aangepaste eigenschappen voor hening en creëert een schone, donkere GitHub-geïnspireerde look:

```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 ... */
```

Geen preprocessor. Geen bouwstap. Geen duizenden hulpprogramma's. Alleen schone, leesbare CSS die werkt.

## Bonus Feature: MetaWeblog API

Voor schrijvers die de voorkeur geven aan speciale markdown editors zoals [Monster markeren](https://markdownmonster.west-wind.com/), het project bevat een volledige MetaWeblog API-implementatie. Deze XML-RPC API laat externe editors toe om:

- Lijstposten
- Nieuwe berichten aanmaken
- Bestaande berichten bewerken
- Posten verwijderen
- Afbeeldingen uploaden
- Categorieën ophalen

De tenuitvoerlegging vindt plaats in de loop van het jaar. `MetaWeblogService.cs` en behandelt het volledige XML-RPC protocol, het verwerken van verzoeken en het genereren van antwoorden. Dit betekent dat u uw blogberichten kunt schrijven in uw favoriete editor en ze direct kunt publiceren naar uw blog.

## Configuratie

Het hele configuratiebestand is slechts 14 regels:

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

- `MarkdownPath` - waar uw markdown bestanden wonen
- `ImagesPath` - waar afbeeldingen worden opgeslagen
- `MetaWeblog` - referenties voor externe editor toegang

## Gebruiken als NuGet-pakket

> Zoals hierboven vermeld zal het spoedig beschikbaar zijn, maar nog niet:)

De blog is nu beschikbaar als een NuGet pakket, waardoor het triviaal om toe te voegen aan een ASP.NET Core applicatie:

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

Dan in uw `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();
```

Dat is het - slechts twee methode oproepen (`AddMinimalBlog` en `UseMinimalBlog`) en je hebt een werkende blog.

## Het voorbeeldproject uitvoeren

Om het meegeleverde steekproefproject uit te voeren:

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

Bezoek `http://localhost:5000` en je ziet de blog met markdown bestanden van het geconfigureerde pad.

## Content aanmaken

Om een nieuwe blogpost aan te maken:

1. Een nieuw aanmaken `.md` bestand in uw geconfigureerd `MarkdownPath`
2. De standaardmetadata toevoegen:
   ```markdown
   # Your Post Title
   
   <!-- category -- YourCategory, AnotherCategory -->
   <datetime class="hidden">2024-11-30T12:00</datetime>
   
   Your content here...
   ```
3. Bestand opslaan
4. De cache verloopt binnen 30 minuten (of herstart de app)

Om afbeeldingen toe te voegen, plaatst u ze gewoon in uw geconfigureerd `ImagesPath` directory en referentie ze in uw markdown:

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

## Wat ontbreekt (On Purpose)

Deze minimale blog bevat niet opzettelijk:

- **Opmerkingen** - Gebruik een service van derden indien nodig
- **Zoeken** - Houd uw inhoud georganiseerd met categorieën
- **Tags** - Categorieën zijn voldoende voor kleine blogs
- **RSS/Atom** - Eenvoudig toe te voegen als je het nodig hebt
- **Authenticatie** - MetaWeblog API gebruikt alleen basis auth
- **Analytics** - Toevoegen JavaScript knipsel indien gewenst
- **SEO-optimalisatie** - Werkt prima met basis meta tags
- **Responsieve afbeeldingen** - Browser handelt het af
- **Donker/licht thema toggle** - Eén thema is genoeg.

Deze functies zijn allemaal *mogelijk* toe te voegen, maar ze zijn niet standaard opgenomen omdat de meeste kleine blogs ze niet nodig hebben.

## Prestatiekenmerken

Ondanks zijn eenvoud, deze blog is **snel**:

- **Geheugencaching** betekent geen bestand I/O na eerste lading
- **Uitvoercaching** betekent geen Razor rendering na eerste verzoek
- **Geen database** betekent geen query overhead
- **Geen JavaScript** betekent snellere paginaladingen
- **Eenvoudig CSS** betekent minimale stylesheet ontleden

Voor een kleine tot middelgrote blog (onder 1000 berichten) zal deze architectuur de meeste door databases ondersteunde blogplatforms overtreffen.

## Wanneer te gebruiken dit vs. de volledige meest lucide blog

Gebruik **Meestal lucid.MinimalBlog** wanneer:

- Je start een persoonlijke blog
- U heeft minder dan 500 posten
- Je hebt geen meerdere talen nodig
- Je wilt het simpel houden.
- U bent comfortabel met markdown bestanden
- Je wilt gewoon schrijven en publiceren

Gebruik de **volledige meest lucide platform** wanneer:

- Je gebruikt je blog als een **leerlaboratorium** voor nieuwe technologieën
- U wilt experimenteren met implementatiestrategieën, monitoring en prestatieoptimalisatie
- Je hebt specifieke functies nodig zoals meertalige ondersteuning, full-text zoeken, of opmerkingen
- Je bouwt pakketten en hebt een echte testbed nodig.
- Je documenteert complexe technische implementaties
- De reis van het bouwen van het platform is net zo waardevol als de inhoud die het host

## Conclusie: Eenvoud als kenmerk

In de moderne web development wereld, bereiken we vaak standaard complexe oplossingen. Heb je een blog nodig? Beter een database opzetten, een ORM configureren, migraties instellen, caching toevoegen, zoeken implementeren, achtergrondtaken configureren...

Maar soms is de eenvoudige oplossing de *rechts* oplossing. Meestallucid.MinimalBlog bewijst dat u een functionele, snelle en onderhoudbare blog platform kunt bouwen met:

- **342 lijnen van C#** (MarkdownBlogService + MetaWeblogService + Programma.cs)
- **~120 lijnen van Razor markup** (4 bladzijden)
- **55 lijnen van CSS**
- **1 NuGet afhankelijkheid** (Markdig)

Dat is **minder dan 520 regels code totaal** voor een compleet blogplatform.

Het project dient als zowel een functioneel blogplatform als een herinnering: voordat je complexiteit toevoegt, vraag jezelf af of je het echt nodig hebt. Soms is een map vol markdown-bestanden alles wat je nodig hebt.

U kunt de volledige broncode vinden in de [Meestallucid.MinimalBlog directory](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.MinimalBlog) Ik zal het nuget pakket vrijgeven zodra ik tevreden ben met de code.

Gelukkig bloggen!