Hello everyone, I am the Wolf at the End of the Desert.

The frontend of this blog written in Go by the site owner is quite native, without using any framework. I originally planned to write a detailed post after refactoring, but a group member brought it up today, so I'll write a brief one.
Online access address: https://go.dotnet9.com
Homepage screenshot:

This article is very simple, so here is a table of contents:
- Go Basics
- Go Blog Building Reference
- Go Version Blog Source Code
- Future Plans
1. Go Basics
- Go Chinese Documentation: https://go.p2hp.com/doc/
- Go Web Programming: PDF file shared in QQ group (771992300)
The above URL and PDF are suitable for building a foundation and reinforcing Go's basic syntax. The PDF file should not be the commercially available "Go Web Programming" (by Zheng Zhaoxiong, Singapore), but it is also quite good, explaining many web basics. It is likely a summary by an online expert and worth reading. Below is the PDF's table of contents:

I recommend the above basic resources for those new to Go. Fundamentals are important.
2. Go Blog Building Reference
The refactored blog frontend template and Go version learning resources come from:
- B站 Up主 [码神之路] B站 homepage: https://space.bilibili.com/473844125
- Tutorial title: [码神之路] Native Go Blog Practical Tutorial, a practice-level project tutorial, without using any framework, easy to understand, explained by a senior programmer with ten years of experience
- Video link: https://www.bilibili.com/video/BV1VS4y1F7NM
Video directory as follows:

Screenshot of the blog post accompanying the video:

The video is suitable for learning and hands-on practice, while the accompanying blog posts help verify code correctness. The frontend template accompanying the blog is shared in the Up主's group (group number [718840650]), and it is also uploaded in the site owner's code repository, but with some modifications different from the Up主's. Some personalized changes are as follows:
- The site owner did not add frontend article editing: The Dotnet9 website already has a backend article management function, so there is overlap.
- Different ORM choice: The site owner chose GORM.
- The site owner directly calls the Dotnet9 website backend API for article search, without implementing a separate interface in Go: The Web API and frontend have clear responsibilities, and it also allows sharing with other client interfaces, e.g., the Razor Pages blog frontend also uses the same article search API.
- Others.
3. Go Version Blog Source Code
As the title of the B站 Up主 [码神之路] video tutorial says: "Native Go Blog Practical Tutorial, a practice-level project tutorial, without using any framework, easy to understand", the emphasis is on native. After practice, the site owner found that the Up主's routing approach is similar to ASP.NET Core's Minimal APIs, though the former mainly writes Web (MVC) and the latter writes Web API. Comparing and learning with familiar technologies can deepen understanding. Below is a brief introduction to the Go version blog source code.
3.1 Entry
main.go is the program entry file, equivalent to ASP.NET Core's Program.cs. The file name can be modified as long as the first line of code is package main.
Full code as follows:
package main
import (
"log"
"net/http"
"dotnet9.com/goweb/common"
"dotnet9.com/goweb/router"
)
func init() {
// Template loading: html files, e.g., homepage article list, category article list, header, footer, etc.
common.LoadTemplate()
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
router.Router()
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
The service binds IP and port hardcoded; it could be written in a configuration file. Doesn't it look similar to the following ASP.NET Core Minimal API:
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run("http://localhost:3000");
router.Router() is a simple encapsulation of routing (as below). It resembles app.MapGet("/", () => "Hello World!"); to some extent—like 8 out of 10?:
Go's simple routing encapsulation: router.go
package router
import (
"net/http"
"dotnet9.com/goweb/views"
"dotnet9.com/goweb/api"
)
func Router() {
http.HandleFunc("/", views.HTML.Index)
http.HandleFunc("/c/", views.HTML.Category)
http.HandleFunc("/p/", views.HTML.Detail)
http.HandleFunc("/login", views.HTML.Login)
http.HandleFunc("/api/v1/post", api.API.SaveAndUpdatePost)
http.Handle("/resource/", http.StripPrefix("/resource/", http.FileServer(http.Dir("public/resource/"))))
}
The routing for the homepage code as follows:
./views/index.go
package views
import (
"errors"
"log"
"net/http"
"strconv"
"dotnet9.com/goweb/common"
"dotnet9.com/goweb/service"
)
func (*HTMLApi) Index(w http.ResponseWriter, r *http.Request) {
// Business code here: read article list, bind template
}
3.n Template Binding
Skipping ORM usage etc., let's talk about template binding:
This is Go's article list template: ./template/layout/post-list.html
{{define "post-list"}}
<ul class="post-box">
{{range $index, $elem := .Posts}}
<li class="post-label">
<a href="/p/{{$elem.Slug}}"><h2>{{$elem.Title}}</h2></a>
<p>{{$elem.Description}}</p>
<div class="post-action">
<span>
<i class="iconfont icon-yonghu"></i>
{{$elem.Original}}
</span>
<span>
<i class="iconfont icon-wenjianjia"></i>
{{range $catIndex, $cat := $elem.Categories}}
<a href="/c/{{$cat.Slug}}">{{$cat.Name}}</a>
{{end}}
</span>
<span>
<i class="iconfont icon-riqi"></i>
{{$elem.CreationTime}}
</span>
<span>
<i class="iconfont icon-yanjing"></i>
{{$elem.ViewCount}}
</span>
</div>
</li>
{{end}}
</ul>
{{end}}
Compare with Razor Pages article list template binding:
@page
@inject IOptionsSnapshot<SiteOptions> SiteOptions
@using Dotnet9.Web.ViewModel.Commons
@model IndexModel
@{
ViewData["Title"] = "首页";
}
<ul class="post-box">
@if (Model.BlogPosts?.Any() == false)
{
<span>没有数据哦</span>
}
else
{
@foreach (var blogPost in Model.BlogPosts!)
{
<li class="post-label">
<a href="@blogPost.CreationTime.ToString("/yyyy/dd")/@blogPost.Slug">
<h2>@blogPost.Title</h2>
</a>
<p>@blogPost.Description</p>
<div class="post-action">
<span>
<i class="iconfont icon-yonghu"></i>
@(blogPost.Original.IsNullOrWhiteSpace() ? SiteOptions.Value.Owner : blogPost.Original)
</span>
<span>
<i class="iconfont icon-wenjianjia"></i>
@foreach (var cat in blogPost.Categories)
{
<a href="/cat/@cat.Slug" title="@cat.Description">@cat.Name</a>
}
</span>
<span>
<i class="iconfont icon-riqi"></i>
@blogPost.CreationTime.ToString("yyyy-MM-dd")
</span>
<span>
<i class="iconfont icon-yanjing"></i>
@blogPost.ViewCount
</span>
</div>
</li>
}
}
</ul>
@await Html.PartialAsync("_PaginationPartial", new PaginationModel(Model.Current, Model.Pages, Model.Total, Model.PageSize, Model.PageCount))
Just a rough look:
- Go template binding uses
{{.object field}}, note the dot before. - Razor syntax binding uses
@Model.object.
Languages are similar; if you're familiar with one tech stack, you can learn others by analogy.
3.n+1 Comments
Comments use the third-party interface Valine. We may develop our own later:
Valine - A fast, concise, and efficient backendless comment system.
Features
- Fast
- Secure
- Emoji 😉
- No backend implementation
- Full MarkDown syntax support
- Lightweight and easy to use
- Article view count statistics v1.2.0+
Website screenshot:

Valine comment backend management:

4. Future Plans
Learning Go, or any new technology, requires not just studying but also finding opportunities to use it. Try to build a project you're interested in using Go, like a simple blog frontend. This will motivate you to learn more. Initially, try native development (without frameworks) to better understand the language's basic syntax.
When you need rapid business development and iteration, consider frameworks. The site owner has recently been interested in goframe:
GoFrame is a modular, high-performance, enterprise-level Go basic development framework. GoFrame is a general-purpose basic development framework, an enhanced extension of the Go standard library, containing common core basic development components. Its advantages include practicality, modularity, comprehensive documentation, rich modules, high ease of use, strong generality, and team-oriented design. If you want to build a business project in Go, whether small, medium, or large, GoFrame is your best choice. If you want to develop a Go component library, GoFrame's out-of-the-box, rich and powerful basic component library will also help you work efficiently. If you are a team leader, GoFrame's extensive documentation, detailed code comments, and active community members will greatly reduce your guidance costs, supporting rapid team onboarding, language transition, and capability improvement.
GoFrame official documentation: https://goframe.org/pages/viewpage.action?pageId=1114119

I'll use goframe to refactor this Go blog later, but of course, I need to finish developing the Razor Pages version first, including the backend…
End of this filler article (took 2 hours). As long as you're willing to spend time learning, there's nothing you can't learn. Just one word: go for it.