455 lines
42 KiB
HTML
455 lines
42 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en-us">
|
||
<head>
|
||
<link rel="preload" href="/lib/font-awesome/webfonts/fa-brands-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<link rel="preload" href="/lib/font-awesome/webfonts/fa-regular-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<link rel="preload" href="/lib/font-awesome/webfonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<link rel="preload" href="/lib/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||
<script type="text/javascript" src="https://latest.cactus.chat/cactus.js"></script>
|
||
<link rel="stylesheet" href="https://latest.cactus.chat/style.css" type="text/css">
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<title> Writing a Middleware for your HTTP handler – Golang | Basit's Blog</title>
|
||
<link rel = 'canonical' href = 'https://blog.rjbasitali.com/posts/writing-a-golang-middleware-for-your-http-handler/'>
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="robots" content="all,follow">
|
||
<meta name="googlebot" content="index,follow,snippet,archive">
|
||
<meta property="og:title" content="Writing a Middleware for your HTTP handler – Golang" />
|
||
<meta property="og:description" content="If you are a backend developer working daily with HTTP requests then you have most likely already encountered situations where you want a common functionality across all the incoming HTTP requests, which can be as simple as checking if the Content-Type header only has the value application/json if you only support json, or maybe you want to spoof your HTTP request to change the method type from POST,GET or PUT to something else based on the X-HTTP-Method-Override header, or of course authenticate before finally passing the request to the destination HTTP handler." />
|
||
<meta property="og:type" content="article" />
|
||
<meta property="og:url" content="https://blog.rjbasitali.com/posts/writing-a-golang-middleware-for-your-http-handler/" /><meta property="article:section" content="posts" />
|
||
<meta property="article:published_time" content="2021-08-01T22:05:04+05:00" />
|
||
<meta property="article:modified_time" content="2021-08-01T22:05:04+05:00" />
|
||
|
||
|
||
<meta name="twitter:card" content="summary"/>
|
||
<meta name="twitter:title" content="Writing a Middleware for your HTTP handler – Golang"/>
|
||
<meta name="twitter:description" content="If you are a backend developer working daily with HTTP requests then you have most likely already encountered situations where you want a common functionality across all the incoming HTTP requests, which can be as simple as checking if the Content-Type header only has the value application/json if you only support json, or maybe you want to spoof your HTTP request to change the method type from POST,GET or PUT to something else based on the X-HTTP-Method-Override header, or of course authenticate before finally passing the request to the destination HTTP handler."/>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="https://blog.rjbasitali.com/css/styles.94f653e9e151e28067a7c5dbbc4600cbd5a3c721e79faaf971e523c40f3b249b8e4f20bb57810dfffa8d559ca5c140fd56eb4cd9c0853113ad08e66afdb08bdd.css" integrity="sha512-lPZT6eFR4oBnp8XbvEYAy9WjxyHnn6r5ceUjxA87JJuOTyC7V4EN//qNVZylwUD9VutM2cCFMROtCOZq/bCL3Q==">
|
||
|
||
|
||
|
||
|
||
<!--[if lt IE 9]>
|
||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||
<![endif]-->
|
||
|
||
|
||
|
||
<link rel="icon" type="image/png" href="https://blog.rjbasitali.com/images/favicon.ico" />
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
<body class="max-width mx-auto px3 ltr">
|
||
<div class="content index py4">
|
||
|
||
<div id="header-post">
|
||
<a id="menu-icon" href="#"><i class="fas fa-bars fa-lg"></i></a>
|
||
<a id="menu-icon-tablet" href="#"><i class="fas fa-bars fa-lg"></i></a>
|
||
<a id="top-icon-tablet" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;" aria-label="Top of Page"><i class="fas fa-chevron-up fa-lg"></i></a>
|
||
<span id="menu">
|
||
<span id="nav">
|
||
<ul>
|
||
|
||
<li><a href="/">Home</a></li>
|
||
|
||
<li><a href="/tags">Tags</a></li>
|
||
|
||
<li><a href="/about">About</a></li>
|
||
|
||
</ul>
|
||
</span>
|
||
<br/>
|
||
<span id="actions">
|
||
<ul>
|
||
|
||
<li>
|
||
<a class="icon" href=" https://blog.rjbasitali.com/contact/" aria-label="Previous">
|
||
<i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i>
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a class="icon" href="https://blog.rjbasitali.com/posts/writing-your-own-reverse-proxy-using-golang/" aria-label="Next">
|
||
<i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i>
|
||
</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" aria-label="Top of Page">
|
||
<i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="#" aria-label="Share">
|
||
<i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
<span id="i-prev" class="info" style="display:none;">Previous post</span>
|
||
<span id="i-next" class="info" style="display:none;">Next post</span>
|
||
<span id="i-top" class="info" style="display:none;">Back to top</span>
|
||
<span id="i-share" class="info" style="display:none;">Share post</span>
|
||
</span>
|
||
<br/>
|
||
<div id="share" style="display: none">
|
||
|
||
<ul>
|
||
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a class="icon" href="http://www.facebook.com/sharer.php?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f" aria-label="Facebook">
|
||
<i class="fab fa-facebook " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://twitter.com/share?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&text=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Twitter">
|
||
<i class="fab fa-twitter " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.linkedin.com/shareArticle?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&title=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Linkedin">
|
||
<i class="fab fa-linkedin " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&is_video=false&description=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Pinterest">
|
||
<i class="fab fa-pinterest " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="mailto:?subject=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang&body=Check out this article: https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f" aria-label="Email">
|
||
<i class="fas fa-envelope " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://getpocket.com/save?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&title=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Pocket">
|
||
<i class="fab fa-get-pocket " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://reddit.com/submit?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&title=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="reddit">
|
||
<i class="fab fa-reddit " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.tumblr.com/share/link?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&name=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang&description=If%20you%20are%20a%20backend%20developer%20working%20daily%20with%20HTTP%20requests%20then%20you%20have%20most%20likely%20already%20encountered%20situations%20where%20you%20want%20a%20common%20functionality%20across%20all%20the%20incoming%20HTTP%20requests%2c%20which%20can%20be%20as%20simple%20as%20checking%20if%20the%20Content-Type%20header%20only%20has%20the%20value%20application%2fjson%20if%20you%20only%20support%20json%2c%20or%20maybe%20you%20want%20to%20spoof%20your%20HTTP%20request%20to%20change%20the%20method%20type%20from%20POST%2cGET%20or%20PUT%20to%20something%20else%20based%20on%20the%20X-HTTP-Method-Override%20header%2c%20or%20of%20course%20authenticate%20before%20finally%20passing%20the%20request%20to%20the%20destination%20HTTP%20handler." aria-label="Tumblr">
|
||
<i class="fab fa-tumblr " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://news.ycombinator.com/submitlink?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&t=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Hacker News">
|
||
<i class="fab fa-hacker-news " aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div id="toc">
|
||
<nav id="TableOfContents"></nav>
|
||
</div>
|
||
|
||
</span>
|
||
</div>
|
||
|
||
|
||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||
<header>
|
||
<h1 class="posttitle" itemprop="name headline">
|
||
Writing a Middleware for your HTTP handler – Golang
|
||
</h1>
|
||
<div class="meta">
|
||
|
||
<div class="postdate">
|
||
|
||
<time datetime="2021-08-01 22:05:04 +0500 PKT" itemprop="datePublished">2021-08-01</time>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="article-read-time">
|
||
<i class="far fa-clock"></i>
|
||
|
||
4 minute read
|
||
</div>
|
||
|
||
|
||
|
||
<div class="article-tag">
|
||
<i class="fas fa-tag"></i>
|
||
|
||
|
||
<a class="tag-link" href="/tags/golang" rel="tag">golang</a>
|
||
|
||
,
|
||
<a class="tag-link" href="/tags/middleware" rel="tag">middleware</a>
|
||
|
||
,
|
||
<a class="tag-link" href="/tags/http" rel="tag">http</a>
|
||
|
||
,
|
||
<a class="tag-link" href="/tags/handler" rel="tag">handler</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</header>
|
||
|
||
|
||
|
||
<div class="content" itemprop="articleBody">
|
||
<p>If you are a backend developer working daily with HTTP requests then you have most likely already encountered situations where you want a common functionality across all the incoming HTTP requests, which can be as simple as checking if the <code>Content-Type</code> header only has the value <code>application/json</code> if you only support json, or maybe you want to spoof your HTTP request to change the method type from <code>POST</code>,<code>GET</code> or <code>PUT</code> to something else based on the <code>X-HTTP-Method-Override</code> header, or of course authenticate before finally passing the request to the destination HTTP handler.</p>
|
||
<p>You can achieve the following behaviour by writing a <code>middleware</code>, also known as a <code>filter</code> in some other backend frameworks. You can have as many middlewares as you want, each with a separate responsibility, and can chain them together to funnel incoming HTTP requests.</p>
|
||
<p>Writing a <code>middleware</code> in Go is pretty simple, you just need to <strong>wrap</strong> your <code>middleware</code> around the base HTTP handler, which so to speak is a thin <strong>wrapper</strong> around your HTTP handler.</p>
|
||
<p>Lets start with <code>http</code> package’s <code>ListenAndServe</code> method, which listens for incoming connections and serves with the handler to handle the requests, and lets write a handler for root <code>"/"</code> path which checks for the header <code>Content-Type</code> to see if it’s <code>application/json</code>, because our API only accepts JSON, and respond with following json <code>{"msg":"Hello world!"}</code> to any incoming request:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">main</span>() {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span> mux <span style="color:#ff79c6">:=</span> http.<span style="color:#50fa7b">NewServeMux</span>()
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span> mux.<span style="color:#50fa7b">HandleFunc</span>(<span style="color:#f1fa8c">"/"</span>, rootHandler)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":8080"</span>, mux)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">rootHandler</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span> <span style="color:#ff79c6">if</span> r.Header.<span style="color:#50fa7b">Get</span>(<span style="color:#f1fa8c">"Content-Type"</span>) <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">"application/json"</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span> http.<span style="color:#50fa7b">Error</span>(w, <span style="color:#f1fa8c">"Only application/json content type supported"</span>, http.StatusUnsupportedMediaType)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span> <span style="color:#ff79c6">return</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span> w.<span style="color:#50fa7b">Write</span>([]<span style="color:#8be9fd;font-style:italic">byte</span>(<span style="color:#f1fa8c">`{"msg":"Hello world!"}`</span>))
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span> }
|
||
</span></span></code></pre></div><p>Of course in reality you could have a dozen of handlers each with a different endpoint, but lets keep it simple for this tutorial.</p>
|
||
<p>Now let’s assume we have a dozen request handlers in our project and we want each handler to check for <code>Content-Type</code> header as we did in our <code>rootHandler</code>, we want our code to be DRY <em>(Don’t Repeat Yourself)</em>.</p>
|
||
<p>Which brings us to writing a <code>middleware</code> to check the <code>Content-Type</code> header for each incoming request. Lets write a middleware and call it <code>wrap</code>:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">wrap</span>(next http.Handler) http.Handler {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span> <span style="color:#ff79c6">return</span> http.<span style="color:#50fa7b">HandlerFunc</span>(<span style="color:#8be9fd;font-style:italic">func</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span> <span style="color:#6272a4">// middleware logic
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span><span style="color:#6272a4"></span> next.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span> })
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6</span><span> }
|
||
</span></span></code></pre></div><p>The <code>wrap</code> function takes in a <code>Handler</code> and returns a <code>Handler</code>, this syntax allows it to be used where we would normally use a request handler, and also allow us to chain multiple middlewares together. Now we can do something like <code>wrap(handler)</code> instead of just using <code>handler</code>, this would call our <code>middleware</code> logic before calling <code>next.ServeHTTP(w, r)</code> which can be the next middleware in the chain or a final call to our request handler.</p>
|
||
<p>In the following example, we are wrapping our <code>wrap</code> middleware around <code>mux</code> so every incoming request will pass through our middleware first:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">main</span>() {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span> mux <span style="color:#ff79c6">:=</span> http.<span style="color:#50fa7b">NewServeMux</span>()
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span> mux.<span style="color:#50fa7b">HandleFunc</span>(<span style="color:#f1fa8c">"/"</span>, rootHandler)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":8080"</span>, <span style="color:#50fa7b">wrap</span>(mux))
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">rootHandler</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span> w.<span style="color:#50fa7b">Write</span>([]<span style="color:#8be9fd;font-style:italic">byte</span>(<span style="color:#f1fa8c">`{"msg":"Hello world!"}`</span>))
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">wrap</span>(next http.Handler) http.Handler {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span> <span style="color:#ff79c6">return</span> http.<span style="color:#50fa7b">HandlerFunc</span>(<span style="color:#8be9fd;font-style:italic">func</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span> <span style="color:#ff79c6">if</span> r.Header.<span style="color:#50fa7b">Get</span>(<span style="color:#f1fa8c">"Content-Type"</span>) <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">"application/json"</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span> http.<span style="color:#50fa7b">Error</span>(w, <span style="color:#f1fa8c">"Only application/json content type supported"</span>, http.StatusUnsupportedMediaType)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span> <span style="color:#ff79c6">return</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span> next.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span> })
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span> }
|
||
</span></span></code></pre></div><p>So far we’ve been using a single middleware, lets see multiple middlewares chained together in action. We can define multiple middlewares with the same signature as of <code>wrap</code>, replacing the comment <code>// middleware logic</code> with our code.</p>
|
||
<blockquote>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">wrap</span>(next http.Handler) http.Handler {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span> <span style="color:#ff79c6">return</span> http.<span style="color:#50fa7b">HandlerFunc</span>(<span style="color:#8be9fd;font-style:italic">func</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span> <span style="color:#6272a4">// middleware logic
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span><span style="color:#6272a4"></span> next.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span> })
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6</span><span> }
|
||
</span></span></code></pre></div></blockquote>
|
||
<p>Lets rename <code>wrap</code> to <code>enforceJSON</code>, and write another <code>middleware</code> which will log every incoming request’s method and path, and chain them both together:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">main</span>() {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span> mux <span style="color:#ff79c6">:=</span> http.<span style="color:#50fa7b">NewServeMux</span>()
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span> mux.<span style="color:#50fa7b">HandleFunc</span>(<span style="color:#f1fa8c">"/"</span>, rootHandler)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":8080"</span>, <span style="color:#50fa7b">logRequest</span>(<span style="color:#50fa7b">enforceJSON</span>(mux)))
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">rootHandler</span>(w ResponseWriter, r <span style="color:#ff79c6">*</span>Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span> w.<span style="color:#50fa7b">Write</span>([]<span style="color:#8be9fd;font-style:italic">byte</span>(<span style="color:#f1fa8c">`{"msg":"Hello world!"}`</span>))
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">logRequest</span>(next http.Handler) http.Handler {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span> <span style="color:#ff79c6">return</span> http.<span style="color:#50fa7b">HandlerFunc</span>(<span style="color:#8be9fd;font-style:italic">func</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span> fmt.<span style="color:#50fa7b">Println</span>(r.Method, r.URL.Path) <span style="color:#6272a4">// logs: GET /v1/users/1234
|
||
</span></span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span><span style="color:#6272a4"></span> next.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span> })
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">enforceJSON</span>(next http.Handler) http.Handler {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span> <span style="color:#ff79c6">return</span> http.<span style="color:#50fa7b">HandlerFunc</span>(<span style="color:#8be9fd;font-style:italic">func</span>(w http.ResponseWriter, r <span style="color:#ff79c6">*</span>http.Request) {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span> <span style="color:#ff79c6">if</span> r.Header.<span style="color:#50fa7b">Get</span>(<span style="color:#f1fa8c">"Content-Type"</span>) <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">"application/json"</span> {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span> http.<span style="color:#50fa7b">Error</span>(w, <span style="color:#f1fa8c">"Only application/json content type supported"</span>, http.StatusUnsupportedMediaType)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span> <span style="color:#ff79c6">return</span>
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span> }
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span> next.<span style="color:#50fa7b">ServeHTTP</span>(w, r)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25</span><span> })
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26</span><span> }
|
||
</span></span></code></pre></div><p>See how we are using <code>logRequest(enforceJSON(mux))</code> instead of <code>wrap(mux)</code>? Now every incoming HTTP request will be logged and then checked for <code>Content-Type</code> before being passed onto the handler.</p>
|
||
<p>A cleaner way to chain multiple handlers is writing a single <code>wrap</code> middleware and chain all the middlewares inside <code>wrap</code>:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span> <span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">wrap</span>(handler http.Handler) http.Handler {
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span> handler = <span style="color:#50fa7b">enforceJSON</span>(handler)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span> handler = <span style="color:#50fa7b">logRequest</span>(handler)
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span> <span style="color:#ff79c6">return</span> handler
|
||
</span></span><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span> }
|
||
</span></span></code></pre></div><p>And then instead of:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":8080"</span>, <span style="color:#50fa7b">logRequest</span>(<span style="color:#50fa7b">enforceJSON</span>(mux)))
|
||
</span></span></code></pre></div><p>We can use:</p>
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span> http.<span style="color:#50fa7b">ListenAndServe</span>(<span style="color:#f1fa8c">":8080"</span>, <span style="color:#50fa7b">wrap</span>(mux))
|
||
</span></span></code></pre></div><hr>
|
||
<p>That’s all on middlewares in golang today. Hope it was useful with your understanding on middlewares or how to write them.</p>
|
||
|
||
</div>
|
||
</article>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div id="footer-post-container">
|
||
<div id="footer-post">
|
||
|
||
<div id="nav-footer" style="display: none">
|
||
<ul>
|
||
|
||
<li><a href="/">Home</a></li>
|
||
|
||
<li><a href="/tags">Tags</a></li>
|
||
|
||
<li><a href="/about">About</a></li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
<div id="toc-footer" style="display: none">
|
||
<nav id="TableOfContents"></nav>
|
||
</div>
|
||
|
||
|
||
<div id="share-footer" style="display: none">
|
||
|
||
<ul>
|
||
|
||
|
||
|
||
|
||
|
||
<li>
|
||
<a class="icon" href="http://www.facebook.com/sharer.php?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f" aria-label="Facebook">
|
||
<i class="fab fa-facebook fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://twitter.com/share?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&text=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Twitter">
|
||
<i class="fab fa-twitter fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.linkedin.com/shareArticle?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&title=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Linkedin">
|
||
<i class="fab fa-linkedin fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&is_video=false&description=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Pinterest">
|
||
<i class="fab fa-pinterest fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="mailto:?subject=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang&body=Check out this article: https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f" aria-label="Email">
|
||
<i class="fas fa-envelope fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://getpocket.com/save?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&title=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Pocket">
|
||
<i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://reddit.com/submit?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&title=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="reddit">
|
||
<i class="fab fa-reddit fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="http://www.tumblr.com/share/link?url=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&name=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang&description=If%20you%20are%20a%20backend%20developer%20working%20daily%20with%20HTTP%20requests%20then%20you%20have%20most%20likely%20already%20encountered%20situations%20where%20you%20want%20a%20common%20functionality%20across%20all%20the%20incoming%20HTTP%20requests%2c%20which%20can%20be%20as%20simple%20as%20checking%20if%20the%20Content-Type%20header%20only%20has%20the%20value%20application%2fjson%20if%20you%20only%20support%20json%2c%20or%20maybe%20you%20want%20to%20spoof%20your%20HTTP%20request%20to%20change%20the%20method%20type%20from%20POST%2cGET%20or%20PUT%20to%20something%20else%20based%20on%20the%20X-HTTP-Method-Override%20header%2c%20or%20of%20course%20authenticate%20before%20finally%20passing%20the%20request%20to%20the%20destination%20HTTP%20handler." aria-label="Tumblr">
|
||
<i class="fab fa-tumblr fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="icon" href="https://news.ycombinator.com/submitlink?u=https%3a%2f%2fblog.rjbasitali.com%2fposts%2fwriting-a-golang-middleware-for-your-http-handler%2f&t=Writing%20a%20Middleware%20for%20your%20HTTP%20handler%20%e2%80%93%20Golang" aria-label="Hacker News">
|
||
<i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div id="actions-footer">
|
||
|
||
<a id="menu-toggle" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;" aria-label="Menu">
|
||
<i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
|
||
|
||
<a id="toc-toggle" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;" aria-label="TOC">
|
||
<i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
|
||
|
||
<a id="share-toggle" class="icon" href="#" onclick="$('#share-footer').toggle();return false;" aria-label="Share">
|
||
<i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> share</a>
|
||
<a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" aria-label="Top of Page">
|
||
<i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<footer id="footer">
|
||
<div class="footer-left">
|
||
Copyright © 2022 Basit Ali
|
||
</div>
|
||
<div class="footer-right">
|
||
<nav>
|
||
<ul>
|
||
|
||
<li><a href="/">Home</a></li>
|
||
|
||
<li><a href="/tags">Tags</a></li>
|
||
|
||
<li><a href="/about">About</a></li>
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</footer>
|
||
|
||
|
||
</div>
|
||
</body>
|
||
|
||
<link rel="stylesheet" href=/lib/font-awesome/css/all.min.css>
|
||
<script src=/lib/jquery/jquery.min.js></script>
|
||
<script src=/js/main.js></script>
|
||
|
||
<script src=/js/code-copy.js></script>
|
||
|
||
|
||
|
||
|
||
</html>
|