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")
})
...
}
Therefore, I need to route requests for each HTTP method myself as follows.
main.go
...
func main() {
...
http.HandleFunc("/webrequest", webRequestHandler)
...
}
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
}
}
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
}
- url package - net/url - pkg.go.dev
- net/http package get Query Params in Go(Golang) - Welcome to Golang by Example
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
...
}
}
...
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));
}
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
}
}
...
Result
{
"message": "Hello_1",
"succeeded": true,
"errorMessage": ""
}
structs for json.Marshal & json.Unmarshal
I defined "sampleResult" like below.
type sampleResult struct {
message string
succeeded bool
errorMessage string
}
Because "json.Marshal" and "json.Unmarshal" ignore private properties, the results always became empty values :P
- json package - encoding/json - pkg.go.dev
- Return JSON body in HTTP response in Go (Golang) - Welcome To Golang By Example
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
...
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
...
But the downloaded files were always named like "webrequest", so I changed to use "http.ServeContent".
Top comments (0)