Attempting to Learn Go - Building a Downloader Part 02
Tags: go, continuingeducation, atlg
Published: 2018-01-11 05:49:53

---
title: "Attempting to Learn Go - Building a Downloader Part 02"
published: true
tags: go,golang,continuing-education, atlg
canonical_url: https://shindakun.glitch.me/attempting-to-learn-go-building-a-downloader-part-02/
cover_image: https://shindakun.glitch.me/content/images/2018/01/attemptinggo-2.png
series: "Attempting to Learn Go"
---
[Last time](https://dev.to/shindakun/attempting-to-learn-go---building-a-downloader-part-01-44gl) we outlined our plan to create a simple Go server that would download remote files. What I'd like to do this time is to begin to write some actual code. I'm going to try and document exactly how I put the program together along with any references I used. I've also tried to be explicit with regards to naming my variables. For instance, using `response http.ResponseWriter, request *http.Request` instead of `w http.ResponseWriter, r *http.Request`. The hope is that anyone of any skill will be able to follow the code.

## Pass One

Our first task is simply to get some code on the screen. We're going to define our package, `main` and a single import `fmt` (format) so we can print to standard output.

```go
package main

import "fmt"
```

We'll use the JSON object we defined previously and [JSON-to-Go](https://mholt.github.io/json-to-go/) website to quickly create our struct. I recommend bookmarking this site, if you are going to work with JSON in Go it comes in very handy.

```go
type download struct {  
  Title string `json:"title"`
  Location string `json:"location"`
}
```

In our main function, for now, we're first going to simply print "Downloader" at startup. After that, we're going to create a test struct which will then be printed.

```go
func main() {  
  fmt.Printf("Downloader")

  download := download{
    Title: "title-test",
    Location: "location-test",
  }

  fmt.Printf("%v", download)
}
```

Which, when run, results in the following output.

```bash
$ go run downloader.go
Downloader  
{title-test location-test}
```

* * *

## Pass Two

The first thing to take note of is that we are using two new imports. [io/ioutil](https://golang.org/pkg/net/http/) and [net/http](https://golang.org/pkg/net/http/) are both parts of the Go standard library and will help take care of some of the functionality. The standard library gives the developer the raw tools needed to create many different programs. All they need to do is learn how to put it together. And as we'll see in a future post if a standard library package doesn't exist for a function there is a good chance someone in the community has thought of that.

```go
package main

import (  
  "fmt"
  "io/ioutil"
  "net/http"
)

type download struct {  
  Title string `json:"title"`
  Location string `json:"location"`
}
```

We come now to our first new function, `status`. Currently, requesting "/" on the server will result in a 200 response with a body of "Hello!". Eventually, we will update this to return a little bit of server information for service health check purposes.

```go
func status(response http.ResponseWriter, request *http.Request) {  
  fmt.Fprintf(response, "Hello!")
}
```

The next new function `handleDownloadRequest` is going to be the main focus of the server portion of our program. In its current form, it will accept a request on "/download" and print the body from the request to the console and the word "Download!" to the browser.

```go
func handleDownloadRequeset(response http.ResponseWriter, request *http.Request) {  
  r, err := ioutil.ReadAll(request.Body)
  if err != nil {
    fmt.Println(err)
  }
  defer request.Body.Close()
  fmt.Println(string(r))

  fmt.Fprintf(response, "Download!")
}
```

To make use of our new functions we've updated `main` to set up the HTTP handlers. Finally, we start the server listening on port 3000.

```go
func main() {  
  fmt.Println("Downloader")

  http.HandleFunc("/", status)
  http.HandleFunc("/download", handleDownloadRequest)
  http.ListenAndServe(":3000", nil)
}
```

* * *

## Pass Three

The majority of changes this time around occur in the `handleDownloadRequest` function. However, if you look closely you'll see that I've also added [encoding/json](https://golang.org/pkg/encoding/json) and [log](https://golang.org/pkg/log) as imports. Being a server application I wanted to use the built-in features of `log` to print to the console. I'm not sure if I'm going to redirect it to file or just leave it on the console at this point though.

```go
package main

import (  
  "encoding/json"
  "fmt"
  "io/ioutil"
  "log"
  "net/http"
)

type download struct {  
  Title string `json:"title"`
  Location string `json:"location"`
}

func status(response http.ResponseWriter, request *http.Request) {  
  fmt.Fprintf(response, "Hello!")
}
```

Currently, the heart of our program resides here. We have what looks to be some significant changes. First, we are creating a struct called `downloadReqest` to hold our incoming title and URL. I've also added some better error handling, with actual responses to the browser when appropriate.

```go
func handleDownloadRequest(response http.ResponseWriter, request *http.Request) {  
  var downloadRequest download
  r, err := ioutil.ReadAll(request.Body)
  if err != nil {
    http.Error(response, "bad request", 400)
    log.Println(err)
    return
  }
  defer request.Body.Close()
```

Assuming the initial request is OK and we are able to read the body, we then need to get the request JSON into our struct. `json.Unmarshal` will take care of the heavy lifting for us. If not we can return an error to the browser, here we include the error message from `json.Unmarshal` in the output.

```go
err = json.Unmarshal(r, &downloadRequest)
  if err != nil {
    http.Error(response, "bad request: "+err.Error(), 400)
    log.Println(err)
    return
  }
  log.Printf("%#v", downloadRequest)

  fmt.Fprintf(response, "Download!")
}

func main() {  
  log.Println("Downloader")

  http.HandleFunc("/", status)
  http.HandleFunc("/download", handleDownloadRequest)
  http.ListenAndServe(":3000", nil)
}
```

Not bad for a little bit of work. We have gone from a basic skeleton to a server in no time. Starting tomorrow and our next revision we'll update the `handleDownloadRequest` function by adding a `getFile` function that we can call to get our actual file.

Until next time...

***

You can find the code for this and most of the other Attempting to Learn Go posts in the repo on GitHub.

{% github shindakun/atlg %}

***

| Enjoy this post? |
|:----:|
| [How about buying me a coffee?](https://ko-fi.com/shindakun) |

***