go
56 lines · 8 steps
Rendering HTML pages with Gin handlers
Two Gin handlers fetch data through a repository and render templates, with graceful error pages when something fails.
Explained by
highlit
1package handlers
2
3import (
4 "net/http"
5 "time"
6
7 "github.com/gin-gonic/gin"
8)
9
10type ArticleHandler struct {
11 Articles ArticleRepository
12}
13
14func (h *ArticleHandler) Index(c *gin.Context) {
15 articles, err := h.Articles.Recent(c.Request.Context(), 20)
16 if err != nil {
17 c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
18 "title": "Something went wrong",
19 "message": "We couldn't load the articles right now.",
20 })
21 return
22 }
23
24 c.HTML(http.StatusOK, "articles/index.tmpl", gin.H{
25 "title": "Latest Articles",
26 "articles": articles,
27 "year": time.Now().Year(),
28 "flash": flashFrom(c),
29 })
30}
31
32func (h *ArticleHandler) Show(c *gin.Context) {
33 article, err := h.Articles.BySlug(c.Request.Context(), c.Param("slug"))
34 if err != nil {
35 c.HTML(http.StatusNotFound, "error.tmpl", gin.H{
36 "title": "Not found",
37 "message": "That article has wandered off.",
38 })
39 return
40 }
41
42 c.HTML(http.StatusOK, "articles/show.tmpl", gin.H{
43 "title": article.Title,
44 "article": article,
45 "year": time.Now().Year(),
46 })
47}
48
49func flashFrom(c *gin.Context) string {
50 if v, ok := c.Get("flash"); ok {
51 if s, ok := v.(string); ok {
52 return s
53 }
54 }
55 return ""
56}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Holding a repository on the handler struct keeps data access injectable and testable instead of hard-wired.
- 2Checking the error before rendering lets each failure return its own status code and friendly page.
- 3A small helper that type-asserts context values isolates fragile lookups so handlers stay readable.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/rendering-html-pages-with-gin-handlers-explained-go-e3bd/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.