<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ggicci</title>
    <description>The latest articles on DEV Community by Ggicci (@ggicci).</description>
    <link>https://dev.to/ggicci</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F692344%2F740a5735-170f-47f3-a41e-7b957cc7a5e1.jpeg</url>
      <title>DEV Community: Ggicci</title>
      <link>https://dev.to/ggicci</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ggicci"/>
    <language>en</language>
    <item>
      <title>Deploying an AES-128 Encrypted HTTP Live Stream (HLS)</title>
      <dc:creator>Ggicci</dc:creator>
      <pubDate>Tue, 01 Mar 2022 10:57:27 +0000</pubDate>
      <link>https://dev.to/ggicci/deploying-an-aes-128-encrypted-http-live-stream-hls-14nn</link>
      <guid>https://dev.to/ggicci/deploying-an-aes-128-encrypted-http-live-stream-hls-14nn</guid>
      <description>&lt;p&gt;&lt;strong&gt;HTTP Live Streaming&lt;/strong&gt; (HLS) is an HTTP-based adaptive bitrate streaming communications protocol developed by Apple Inc. and released in 2009.&lt;/p&gt;

&lt;p&gt;Some key points of HLS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Created by Apple.&lt;/li&gt;
&lt;li&gt;Consists of a playlist/manifest file (e.g. index.m3u8) and segment video files (e.g. index01.ts).&lt;/li&gt;
&lt;li&gt;H264 codec of video + AAC of audio.&lt;/li&gt;
&lt;li&gt;Use HTTP, easily leveraging CDN to reach the widest audience without worring about the bandwidth and firewalls.&lt;/li&gt;
&lt;li&gt;Adaptive streaming, enables changing the quality of the video mid-stream.&lt;/li&gt;
&lt;li&gt;Widely supported across devicies and platforms. PC, mobile, Web, IOS, Andorid, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Let's start from an sample video, which can be downloaded from &lt;a href="https://vimeo.com/347119375"&gt;vimeo&lt;/a&gt;. In this post, we are going to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Convert the sample video from MP4 to HLS (a &lt;code&gt;.m3u8&lt;/code&gt; playlist file and &lt;code&gt;.ts&lt;/code&gt; segment files) with AES-128 encryption method;&lt;/li&gt;
&lt;li&gt;Serve the key files on a &lt;strong&gt;key server&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Serve the HLS files on a &lt;strong&gt;content server&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Test the deployment with &lt;a href="https://www.videolan.org/vlc/"&gt;VLC player&lt;/a&gt; and &lt;a href="https://videojs.com/"&gt;video.js&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🔄 Transcode (MP4 -&amp;gt; HLS)
&lt;/h2&gt;

&lt;p&gt;The original sample video was in &lt;code&gt;.mp4&lt;/code&gt; format. While we need HLS files to serve with. We can use &lt;a href="https://www.ffmpeg.org/"&gt;&lt;code&gt;ffmpeg&lt;/code&gt;&lt;/a&gt; to do the transcoding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without AES-128&lt;/strong&gt;, we can simply run the following command:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/lukebussey/4d27678c72580aeb660c19a6fb73e9ee"&gt;https://gist.github.com/lukebussey/4d27678c72580aeb660c19a6fb73e9ee&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; sample-video.mp4 &lt;span class="nt"&gt;-codec&lt;/span&gt;: copy &lt;span class="nt"&gt;-start_number&lt;/span&gt; 0 &lt;span class="nt"&gt;-hls_time&lt;/span&gt; 10 &lt;span class="nt"&gt;-hls_list_size&lt;/span&gt; 0 &lt;span class="nt"&gt;-f&lt;/span&gt; hls sample-noaes.m3u8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;sample-noaes.m3u8&lt;/code&gt; with VLC player. You should see it playing well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With AES-128&lt;/strong&gt;, we need to firstly generate an encryption key and an optional IV (initialization vector) for the AES algorithm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rand 16 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; enc.key  &lt;span class="c"&gt;# Key to encrypt the video&lt;/span&gt;
openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16       &lt;span class="c"&gt;# IV&lt;/span&gt;
&lt;span class="c"&gt;# de0efc88a53c730aa764648e545e3874&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then make a key info file having the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KEY URI
Path to the key file
IV (optional)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is used by &lt;code&gt;ffmpeg&lt;/code&gt;. e.g. &lt;code&gt;enc.keyinfo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://ksm.ggicci.me/e9672408-b38b-4465-ab47-519c554ae402/enc.key
enc.key
de0efc88a53c730aa764648e545e3874
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first line is the URI of the key. Which will be written to m3u8 file. And the player will consult this URI for the key to decrypt the segments files while playing.&lt;/li&gt;
&lt;li&gt;The second line is the path to the file containg the encryption/decription key.&lt;/li&gt;
&lt;li&gt;The third line is an optional initialization vector.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now then, we can feed it to &lt;code&gt;ffmpeg&lt;/code&gt; to do the transcoding again. But this time, we should get an encrypted version of the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-i&lt;/span&gt; sample-video.mp4 &lt;span class="se"&gt;\ &lt;/span&gt; &lt;span class="c"&gt;# input file&lt;/span&gt;
  &lt;span class="nt"&gt;-hls_time&lt;/span&gt; 9 &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# 9s for each chunk&lt;/span&gt;
  &lt;span class="nt"&gt;-hls_key_info_file&lt;/span&gt; enc.keyinfo &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# encryption key&lt;/span&gt;
  &lt;span class="nt"&gt;-hls_playlist_type&lt;/span&gt; vod &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# video on demand mode&lt;/span&gt;
  &lt;span class="nt"&gt;-hls_segment_filename&lt;/span&gt; &lt;span class="s2"&gt;"sample-%d.ts"&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# name the segment files in pattern&lt;/span&gt;
  sample.m3u8 &lt;span class="c"&gt;# HLS playlist (aka. HLS manifest)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;sample.m3u8&lt;/code&gt; with VLC. It &lt;strong&gt;won't&lt;/strong&gt; play. Because VLC tried to retrieve the key file from this URI &lt;a href="https://ksm.ggicci.me/e9672408-b38b-4465-ab47-519c554ae402/enc.key"&gt;https://ksm.ggicci.me/e9672408-b38b-4465-ab47-519c554ae402/enc.key&lt;/a&gt; as noted in &lt;code&gt;sample.m3u8&lt;/code&gt;. But it failed. Since we don't have this &lt;strong&gt;key server&lt;/strong&gt; ready, yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔑 Start a Key Server
&lt;/h2&gt;

&lt;p&gt;Let's serve our key file &lt;code&gt;enc.key&lt;/code&gt; on a web server and make it accessible from the URI above. Then go back to check if VLC can play this &lt;code&gt;sample.m3u8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I recommend using &lt;a href="https://caddyserver.com/"&gt;Caddy&lt;/a&gt; to start up a web server. Which should be easy to learn. Sample site configuration in the Caddyfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ksm.ggicci.me {
  log {
    level INFO
  }

  tls {
    alpn http/1.1
  }

  file_server {
    root /var/www/ksm.ggicci.me
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy file &lt;code&gt;enc.key&lt;/code&gt; to our web server in the path specified by the URI. And open &lt;code&gt;sample.m3u8&lt;/code&gt; again. This time it should work, since the key URI became accessible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kPINP71W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbzsf9qijfce7b9ipp07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kPINP71W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbzsf9qijfce7b9ipp07.png" alt="VLC Opens HLS Playlist" width="880" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎞️ Start a Content Server
&lt;/h2&gt;

&lt;p&gt;So far, our encryption key file has been served well on our key server. As long as we move our video content to a web server, too. We can consume our videos online with assistance of video players. We call it a &lt;strong&gt;Content Server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Again, we use Caddy to achieve our goal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v.ggicci.me {
  log {
    level INFO
  }

  tls {
    alpn http/1.1
  }

  file_server {
    root /var/www/v.ggicci.me
    index index.html
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the playlist file and segment files to the web server. Under &lt;code&gt;/sample&lt;/code&gt; folder in my case. And it's now accessible at &lt;a href="https://v.ggicci.me/sample/index.m3u8"&gt;https://v.ggicci.me/sample/index.m3u8&lt;/a&gt;. Try open it with VLC player (Media &amp;gt; Open Network Stream...). It should work like a charm.&lt;/p&gt;

&lt;h2&gt;
  
  
  📺 Use of video.js
&lt;/h2&gt;

&lt;p&gt;Now that we have set up servers to host our stream. Why not host a video player, too? Thus we can consume our videos through web browsers!&lt;/p&gt;

&lt;p&gt;Here we will try &lt;a href="https://videojs.com/"&gt;video.js&lt;/a&gt; - the world's most popular open source HTML5 player framework.&lt;/p&gt;

&lt;p&gt;With a little research on its official documentation. We can compose a test HTML like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://vjs.zencdn.net/7.11.4/video-js.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"my-video"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"video-js"&lt;/span&gt;
    &lt;span class="na"&gt;controls&lt;/span&gt;
    &lt;span class="na"&gt;preload=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
    &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"420"&lt;/span&gt;
    &lt;span class="na"&gt;poster=&lt;/span&gt;&lt;span class="s"&gt;"/sample/cover.png"&lt;/span&gt;
    &lt;span class="na"&gt;data-setup=&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/sample/index.m3u8"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/x-mpegURL"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"vjs-no-js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      To view this video please enable JavaScript, and consider upgrading to a
      web browser that
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://videojs.com/html5-video-support/"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;supports HTML5 video&lt;span class="nt"&gt;&amp;lt;/a&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/video&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://vjs.zencdn.net/7.11.4/video.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we need to read encryption key files from &lt;code&gt;ksm.ggicci.me&lt;/code&gt; on &lt;code&gt;v.ggicci.me&lt;/code&gt;. We will meet &lt;a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing"&gt;CORS&lt;/a&gt; problem. Solving it by adding corresponding headers to &lt;code&gt;ksm.ggicci.me&lt;/code&gt;'s site configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ksm.ggicci.me {
  log {
    level INFO
  }

  tls {
    alpn http/1.1
  }

  file_server {
    root /var/www/ksm.ggicci.me
  }

  header {
    Vary "Origin"
    Access-Control-Allow-Origin "https://v.ggicci.me"
    Access-Control-Allow-Methods "OPTIONS, HEAD, GET"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="https://v.ggicci.me/sample"&gt;https://v.ggicci.me/sample&lt;/a&gt; to see the result. Also try it with your mobile devices :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DQh4APFd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfv0ldvtisum3zlw558x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DQh4APFd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfv0ldvtisum3zlw558x.png" alt="AES-128 Encrypted HLS Architecture" width="502" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/streaming/"&gt;Apple Developer - HTTP Live Streaming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dacast.com/blog/hls-encryption-for-video/"&gt;HLS Encryption: How to Encrypt Videos in AES-128 For HTTP Live Streaming [2021 Update]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/streaming/fps/"&gt;Apple FPS - FairPlay Streaming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>streaming</category>
      <category>hls</category>
      <category>vod</category>
      <category>ffmpeg</category>
    </item>
    <item>
      <title>Decode HTTP Query Params into a Struct in Go</title>
      <dc:creator>Ggicci</dc:creator>
      <pubDate>Tue, 24 Aug 2021 05:54:34 +0000</pubDate>
      <link>https://dev.to/ggicci/decode-http-query-params-into-a-struct-in-golang-6g5</link>
      <guid>https://dev.to/ggicci/decode-http-query-params-into-a-struct-in-golang-6g5</guid>
      <description>&lt;p&gt;Many people use &lt;code&gt;net/http&lt;/code&gt; package directly in Go to deal with HTTP requests, including reading URL parameters, HTTP headers, and the request body. It's straightforward and efficient, though. We can still get bored writing so much tedious code for just reading and parsing the URL params. Especially when we were maintaining a service with hundres of APIs.&lt;/p&gt;

&lt;p&gt;Let's see a piece of code for dealing with HTTP requests by using &lt;code&gt;net/http&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// GET /v1/users?page=1&amp;amp;per_page=20&amp;amp;is_member=true&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ListUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Invalid parameter: page.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;perPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"per_page"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Invalid parameter: per_page.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;isMember&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"is_member"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Invalid parameter: is_member.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Read database and return the user list.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's okay if there are only a few (usually less than 3) parameters to be parsed. But what if more? Both the number of lots of local variables and the writing of statements of parsing string URL params to target types can kill us. Some "evil" guys even spread these statements all over in an API handler. 🤒&lt;/p&gt;

&lt;p&gt;For such cases, what can we do to save a clean and tidy code base?&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;ggicci/httpin&lt;/code&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/ggicci/httpin"&gt;&lt;strong&gt;httpin&lt;/strong&gt;&lt;/a&gt; - 🍡 HTTP Input for Go - Decode an HTTP request into a custom struct&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;ggicci/httpin&lt;/strong&gt; is an awesome package which helps you easily decoding HTTP reqeusts from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query string (URL parameters), e.g. &lt;code&gt;?name=john&amp;amp;is_member=true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Headers, e.g. &lt;code&gt;Authorization: xxx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Form data, e.g. &lt;code&gt;login=john&amp;amp;password=*****&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;JSON/XML body, e.g. &lt;code&gt;POST {"name": "john", "is_member": true}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Path variables, e.g. &lt;code&gt;/users/{username}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;File uploads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to write parsing code for each parameter by yourself. You only care about the input struct and in which handler it will be used.&lt;/p&gt;

&lt;p&gt;Let's see an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// 1. Define you input struct&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ListUsersInput&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Page&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt;  &lt;span class="s"&gt;`in:"form=page"`&lt;/span&gt;
    &lt;span class="n"&gt;PerPage&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;  &lt;span class="s"&gt;`in:"form=per_page"`&lt;/span&gt;
    &lt;span class="n"&gt;IsMember&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="s"&gt;`in:"form=is_member"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// 2. Bind this input struct with an HTTP handler&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;httpin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListUsersInput&lt;/span&gt;&lt;span class="p"&gt;{}),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ThenFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ListUsers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// 3. Use the input in the handler, all the parsing stuff are handled by httpin&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ListUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ListUsersInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will find that with the help of &lt;strong&gt;httpin&lt;/strong&gt;, you can get the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⌛️ Saving developer time&lt;/li&gt;
&lt;li&gt;♻️ Lower code repetition rate&lt;/li&gt;
&lt;li&gt;📖 Higher readability&lt;/li&gt;
&lt;li&gt;🔨 Higher maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;httpin&lt;/strong&gt; is well documentated at &lt;a href="https://ggicci.github.io/httpin/"&gt;https://ggicci.github.io/httpin/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;❤️ Have a try with it :)&lt;/p&gt;

</description>
      <category>go</category>
      <category>http</category>
      <category>api</category>
    </item>
  </channel>
</rss>
