DEV Community

Masui Masanori
Masui Masanori

Posted on

[Golang] Try handling web requests

#go

Intro

This time, I will try handling web requests.
I will use the project what I created last time.

Handling web requests

First I will try handling web requests.
Because I have never tried accessing database in Golang, I will try GET and POST methods in this time.

Separate operations by HTTP methods

Because "http.Handle" and "http.HandleFunc" don't have functions to specify the HTTP methods, I can't add same routes for each HTTP methods.

main.go

package main

import (
    "html/template"
    "log"
    "net/http"
    "path/filepath"
    "sync"
)
...
func main() {
...
    http.HandleFunc("/webrequest", webRequestHandler)
    // panic: http: multiple registrations for /webrequest
    http.HandleFunc("/webrequest", func(_ http.ResponseWriter, _ *http.Request) {
        log.Println("webrequest2")
    })
...
}
Enter fullscreen mode Exit fullscreen mode

Therefore, I need to route requests for each HTTP method myself as follows.

main.go

...
func main() {
...
    http.HandleFunc("/webrequest", webRequestHandler)
...
}
Enter fullscreen mode Exit fullscreen mode

webRequestSample.go

package main

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

func webRequestHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        // For GET request
        break
    case http.MethodPost:
        // For POST request
        break
    }
}
Enter fullscreen mode Exit fullscreen mode

Get query paramters

I can get query paramters from "http.Request".

webRequestSample.go

...
func webRequestHandler(w http.ResponseWriter, r *http.Request) {
    name, err := getParam(r, "name")
    if err == nil {
        log.Println(name)
    } else {
        log.Println(err)
    }
...
}
func getParam(r *http.Request, key string) (string, error) {
    result := r.URL.Query().Get(key)
    if len(result) <= 0 {
        return "", errors.New(fmt.Sprintf("no value: %s", key))
    }
    return result, nil
}
Enter fullscreen mode Exit fullscreen mode

Return formatted string values

webRequestSample.go

...
func webRequestHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        name, err := getParam(r, "name")
        if err != nil {
            fmt.Fprint(w, "The parameters have no names")
            return
        }
        fmt.Fprintf(w, "Hi there, I love %s!", name)
        break
...
    }
}
...
Enter fullscreen mode Exit fullscreen mode

Return JSON values(+ reading values from body)

webRequest.page.ts

export function send() {
    fetch(targetUrl, 
        {
            method: "POST",
            body: JSON.stringify({ messageType: "text", data: "Hello" }),
        })
        .then(res => res.json())
        .then(json => console.log(json))
        .catch(err => console.error(err));
}
Enter fullscreen mode Exit fullscreen mode

webRequestSample.go

...
type sampleResult struct {
    Message      string `json:"message"`
    Succeeded    bool   `json:"succeeded"`
    ErrorMessage string `json:"errorMessage"`
}

func webRequestHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
...
    case http.MethodPost:
        returnValue := &sampleResult{}
        w.Header().Set("Content-Type", "application/json")
        // read values from HTTP request body
        body, readBodyError := ioutil.ReadAll(r.Body)

        if readBodyError != nil {
            returnValue.Succeeded = false
            returnValue.ErrorMessage = "Failed reading values from body"
            failedReadingData, _ := json.Marshal(returnValue)
            w.Write(failedReadingData)
            return
        }
        wsMessage := &websocketMessage{}
        convertError := json.Unmarshal(body, &wsMessage)
        if convertError != nil {
            returnValue.Succeeded = false
            returnValue.ErrorMessage = "Failed converting to WebSocketMessage"
            failedConvertingData, _ := json.Marshal(returnValue)
            w.Write(failedConvertingData)
            return
        }
        w.WriteHeader(200)
        returnValue.Message = fmt.Sprintf("%s_1", wsMessage.Data)
        returnValue.Succeeded = true
        returnValue.name = "hello world"
        data, _ := json.Marshal(returnValue)

        w.Write(data)
        break
    }
}
...
Enter fullscreen mode Exit fullscreen mode

Result

{
    "message": "Hello_1",
    "succeeded": true,
    "errorMessage": ""
}
Enter fullscreen mode Exit fullscreen mode

structs for json.Marshal & json.Unmarshal

I defined "sampleResult" like below.

type sampleResult struct {
    message      string
    succeeded    bool
    errorMessage string
}
Enter fullscreen mode Exit fullscreen mode

Because "json.Marshal" and "json.Unmarshal" ignore private properties, the results always became empty values :P

Return file data

webRequestSample.go

...
func webRequestHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        name, err := getParam(r, "name")
        if err != nil {
            log.Println(err.Error())
            return
        }
        // Search file from current directory
        cur, _ := os.Getwd()
        file, fileErr := os.Open(fmt.Sprintf("%s/files/%s", cur, name))
        if fileErr != nil {
            log.Println(fileErr.Error())
            w.WriteHeader(404)
            return
        }
        // Get file infomations
        fileinfo, staterr := file.Stat()
        if staterr != nil {
            log.Println(staterr.Error())
            w.WriteHeader(404)
            return
        }
        // To download file, I set "application/octet-stream" as Content-Type
        w.Header().Set("Content-Type", "application/octet-stream")
        // set its file name
        cd := mime.FormatMediaType("attachment", map[string]string{"filename": fileinfo.Name()})
        w.Header().Set("Content-Disposition", cd)
        http.ServeContent(w, r, fileinfo.Name(), fileinfo.ModTime(), file)
        break
...
Enter fullscreen mode Exit fullscreen mode

File name

First, I tried reading the file and return byte array like below.

...
        // Get file infomations
        fileinfo, staterr := file.Stat()
        if staterr != nil {
            log.Println(staterr.Error())
            w.WriteHeader(404)
            return
        }
        // Read file
        fileData := make([]byte, fileinfo.Size())
        fileSize, loadErr := file.Read(fileData)
        if loadErr != nil {
            log.Println(loadErr.Error())
            w.WriteHeader(404)
            return
        }
        // To download file, I set "application/octet-stream" as Content-Type
        w.Header().Set("Content-Type", "application/octet-stream")
        w.Write(fileData)
        break
...
Enter fullscreen mode Exit fullscreen mode

But the downloaded files were always named like "webrequest", so I changed to use "http.ServeContent".

Discussion (0)