diff options
| author | alex emery <[email protected]> | 2024-11-03 15:33:28 +0000 |
|---|---|---|
| committer | alex emery <[email protected]> | 2024-11-03 15:33:28 +0000 |
| commit | 508527f52de524a4fd174d386808e314b4138b11 (patch) | |
| tree | 2593af258b67decbf0207e2547b7ea55f6b051d7 /public/posts/2024-08-26-01.html | |
| parent | 22bfae8f9637633d5608caad3ce56b64c6819505 (diff) | |
feat: static builds
Diffstat (limited to 'public/posts/2024-08-26-01.html')
| -rw-r--r-- | public/posts/2024-08-26-01.html | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/public/posts/2024-08-26-01.html b/public/posts/2024-08-26-01.html new file mode 100644 index 0000000..acf51a6 --- /dev/null +++ b/public/posts/2024-08-26-01.html @@ -0,0 +1,280 @@ + +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <meta name="description" content="Home for a73x" /> + <meta name="author" content="a73x" /> + <meta name="viewport" + content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width" /> + <title>a73x</title> + <link rel="stylesheet" href="/static/styles.css"> + <link rel="stylesheet" href="/static/syntax.css"> +</head> + +<body> + <h1>a73x</h1> + <sub>high effort, low reward</sub> + <nav class="nav"> + <ul> + + + <li><a class="no-decorations" href="/">home</a></li> + + + + <li><a class="no-decorations" href="/posts">posts</a></li> + + + + <li><a class="no-decorations" href="/ethos">ethos</a></li> + + + </ul> + </nav> + +<h1>Writing HTTP Handlers</h1> +<p>I’m sharing how I write handlers in Go.</p> + +<p>I write them like this for reasons that are probably fairly contextual. I’ve written a few applications and had to swap REST libraries or even swapped REST for GRPC, so things that make that easier speak to me a great deal.</p> + +<p>I’ve used <code>ints</code> instead of the <code>http.StatusXXXX</code> and omitted <code>JSON</code> tags in an attempt to try save up screen space.</p> + +<p>To begin with, you might have something like this:</p> +<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"log"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"net/http"</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="s">"Hello, World!"</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="nx">handler</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">":8080"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span></code></pre> +<p>Then you might get told off because you’ve just registered routes with the default mux, which isn’t very testable.</p> + +<p>So you tweak it a little bit.</p> +<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"log"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"net/http"</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handler</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="s">"Hello, World!"</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">newMux</span><span class="p">()</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">ServeMux</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="nx">handler</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">mux</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span> <span class="o">:=</span> <span class="nf">newMux</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">":8080"</span><span class="p">,</span> <span class="nx">mux</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre> +<p><code>newMux()</code> gives you a <code>mux</code> to use when testing.</p> + +<p><code>Run</code> keeps <code>main</code> nice and clean, so you can just return errors as needed instead of going <code>log.Fatal</code> and just generally being messy.</p> + +<p>But now you need to do something real, you want to store and fetch data.</p> +<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span> +</span></span><span class="line"><span class="cl"> <span class="s">"encoding/json"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"log"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"net/http"</span> +</span></span><span class="line"><span class="cl"> <span class="s">"strconv"</span> +</span></span><span class="line"><span class="cl"><span class="p">)</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewMux</span><span class="p">()</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">ServeMux</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="o">:=</span> <span class="nx">Server</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">:</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="nx">Content</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nf">Register</span><span class="p">(</span><span class="nx">mux</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">mux</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span> <span class="o">:=</span> <span class="nf">NewMux</span><span class="p">()</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">":8080"</span><span class="p">,</span> <span class="nx">mux</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Server</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span> <span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="nx">Content</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Register</span><span class="p">(</span><span class="nx">mux</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">ServeMux</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">"GET /{id}"</span><span class="p">,</span> <span class="nx">s</span><span class="p">.</span><span class="nx">Get</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">"POST /"</span><span class="p">,</span> <span class="nx">s</span><span class="p">.</span><span class="nx">Post</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">idStr</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">PathValue</span><span class="p">(</span><span class="s">"id"</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">id</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Atoi</span><span class="p">(</span><span class="nx">idStr</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="mi">400</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"failed to parse id: %v"</span><span class="p">,</span> <span class="nx">err</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">data</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">id</span><span class="p">]</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="s">"not found"</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">NewEncoder</span><span class="p">(</span><span class="nx">w</span><span class="p">).</span><span class="nf">Encode</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ContentPostReq</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Foo</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Post</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">req</span> <span class="o">:=</span> <span class="nx">ContentPostReq</span><span class="p">{}</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">NewDecoder</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">Body</span><span class="p">).</span><span class="nf">Decode</span><span class="p">(</span><span class="o">&</span><span class="nx">req</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="mi">400</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">Write</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"failed to parse request: %v"</span><span class="p">,</span> <span class="nx">err</span><span class="p">)))</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">id</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">data</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nx">Content</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ID</span><span class="p">:</span> <span class="nx">id</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Foo</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">Foo</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">id</span><span class="p">]</span> <span class="p">=</span> <span class="nx">content</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">NewEncoder</span><span class="p">(</span><span class="nx">w</span><span class="p">).</span><span class="nf">Encode</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Content</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ID</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Foo</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre><pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">❯ curl -X POST localhost:8080 --header <span class="s2">"Content-Type: application/json"</span> -d <span class="s1">'{"foo":"bar"}'</span> +</span></span><span class="line"><span class="cl"><span class="o">{</span><span class="s2">"ID"</span>:0,<span class="s2">"Foo"</span>:<span class="s2">"bar"</span><span class="o">}</span> +</span></span><span class="line"><span class="cl">❯ curl -X GET localhost:8080/0 +</span></span><span class="line"><span class="cl"><span class="o">{</span><span class="s2">"ID"</span>:0,<span class="s2">"Foo"</span>:<span class="s2">"bar"</span><span class="o">}</span> +</span></span></code></pre> +<p>Erm, well, okay. Quite a bit has changed here, but I’m sure you can read it. We now save and fetch very, very safely from a map and return the response as <code>JSON</code>. I’ve done some things for brevity because I want to get to the main point.</p> + +<p>This API is inconsistent. It sometimes returns <code>JSON</code>, and the others return strings. Overall, it’s just a mess.</p> + +<p>So let’s try to standardise things. +First, let’s design some form of REST spec.</p> +<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">JSONResp</span><span class="p">[</span><span class="nx">T</span> <span class="nx">any</span><span class="p">]</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Resources</span> <span class="p">[]</span><span class="nx">T</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Errs</span> <span class="p">[]</span><span class="nx">ErrorResp</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorResp</span> <span class="kd">struct</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Status</span> <span class="kt">int</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Msg</span> <span class="kt">string</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre> +<p>We want to be able to support fetching multiple resources at once, if we can only fetch some resources, let’s return them under <code>resources</code> and show the errors under <code>errs</code></p> + +<p>Now, add some helpful functions to handle things.</p> +<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nx">Post</span><span class="p">[</span><span class="nx">In</span> <span class="nx">any</span><span class="p">,</span> <span class="nx">Out</span> <span class="nx">any</span><span class="p">](</span><span class="nx">successCode</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">fn</span> <span class="kd">func</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">In</span><span class="p">)</span> <span class="p">([]</span><span class="nx">Out</span><span class="p">,</span> <span class="p">[]</span><span class="nx">ErrorResp</span><span class="p">))</span> <span class="kd">func</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">v</span> <span class="nx">In</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">NewDecoder</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">Body</span><span class="p">).</span><span class="nf">Decode</span><span class="p">(</span><span class="o">&</span><span class="nx">v</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">writeJSONResp</span><span class="p">[</span><span class="nx">Out</span><span class="p">](</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="p">[]</span><span class="nx">ErrorResp</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Status</span><span class="p">:</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Msg</span><span class="p">:</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"failed to parse request: %v"</span><span class="p">,</span> <span class="nx">err</span><span class="p">),</span> +</span></span><span class="line"><span class="cl"> <span class="p">},</span> +</span></span><span class="line"><span class="cl"> <span class="p">})</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">res</span><span class="p">,</span> <span class="nx">errs</span> <span class="o">:=</span> <span class="nf">fn</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nf">Context</span><span class="p">(),</span> <span class="nx">v</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nf">writeJSONResp</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">successCode</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">errs</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nx">writeJSONResp</span><span class="p">[</span><span class="nx">T</span> <span class="nx">any</span><span class="p">](</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">successCode</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">res</span> <span class="p">[]</span><span class="nx">T</span><span class="p">,</span> <span class="nx">errs</span> <span class="p">[]</span><span class="nx">ErrorResp</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">body</span> <span class="o">:=</span> <span class="nx">JSONResp</span><span class="p">[</span><span class="nx">T</span><span class="p">]{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Resources</span><span class="p">:</span> <span class="nx">res</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Errs</span><span class="p">:</span> <span class="nx">errs</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="nx">status</span> <span class="o">:=</span> <span class="nx">successCode</span> +</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">e</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">errs</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Status</span> <span class="p">></span> <span class="nx">status</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">status</span> <span class="p">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Status</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="nx">status</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">NewEncoder</span><span class="p">(</span><span class="nx">w</span><span class="p">).</span><span class="nf">Encode</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre> +<p>And we’ve standardised all <code>POST</code> requests!</p> + +<p>This function can be used by all <code>POST</code> requests, ensuring they adhere to the spec. It also removes the repetitive code around marshalling and unmarshalling to <code>JSON</code> and handles errors in a consistent manner. +The handler functions accept a <code>context</code> param and their expected struct input.</p> +<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Register</span><span class="p">(</span><span class="nx">mux</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">ServeMux</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"><span class="o">...</span> +</span></span><span class="line"><span class="cl"> <span class="nx">mux</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">"POST /"</span><span class="p">,</span> <span class="nf">Post</span><span class="p">(</span><span class="mi">201</span><span class="p">,</span> <span class="nx">s</span><span class="p">.</span><span class="nx">Post</span><span class="p">))</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Post</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">req</span> <span class="nx">ContentPostReq</span><span class="p">)</span> <span class="p">([]</span><span class="nx">Content</span><span class="p">,</span> <span class="p">[]</span><span class="nx">ErrorResp</span><span class="p">)</span> <span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">id</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">data</span><span class="p">)</span> +</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nx">Content</span><span class="p">{</span> +</span></span><span class="line"><span class="cl"> <span class="nx">ID</span><span class="p">:</span> <span class="nx">id</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="nx">Foo</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">Foo</span><span class="p">,</span> +</span></span><span class="line"><span class="cl"> <span class="p">}</span> +</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="nx">id</span><span class="p">]</span> <span class="p">=</span> <span class="nx">content</span> +</span></span><span class="line"><span class="cl"> +</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">[]</span><span class="nx">Content</span><span class="p">{</span><span class="nx">content</span><span class="p">},</span> <span class="kc">nil</span> +</span></span><span class="line"><span class="cl"><span class="p">}</span> +</span></span></code></pre> +<p>As you can see, the post function is fairly cleaner now.</p> + +<p>You can extend this to all the other request types. If you have query or path parameters, you could either pass in the request, write a custom struct tag parser, or find someone else who has already done it: <a href="https://github.com/gorilla/schema">https://github.com/gorilla/schema</a>.</p> + + + <footer> + <br /> + <hr /><br /> + <p>see something you disagree with? email: <a href="mailto:[email protected]">[email protected]</a></p> + </footer> +</body> + +</html> + |
