なるべくGoのパッケージを使わないで、RESTサーバとクライアントを作って、JSONのデータ送受信してみる

サーバ側 loc_rest_server.go というファイル名で保存して、
>go run loc_rest_server.go
で起動


package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"
)

/*

type GetLoc struct {
	Message string `json:"message"`
	Name    string `json:"name"`
}
*/

/*
// GetLoc GetLoc
type GetLoc struct {
	ID  int64   `json:"id"`
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
	//Address string  `json:"address"`
}
*/

// GetLoc GetLoc
type GetLoc struct {
	ID  string `json:"id"`
	Lat string `json:"lat"`
	Lng string `json:"lng"`
	//Address string  `json:"address"`
}

// ErrorResponse error response
type ErrorResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// locService loc service
func locService(ctx context.Context, number string, tm time.Time) (*GetLoc, error) {

	if number == "1" {
		return &GetLoc{
			ID:  number,
			Lat: "35.653976",
			Lng: "139.796842",
		}, nil
	}

	if number == "2" {
		return &GetLoc{
			ID:  number,
			Lat: "35.653758",
			Lng: "139.794192",
		}, nil
	}

	return nil, nil
}

// AppHandler application handler adaptor
type AppHandler struct {
	h func(http.ResponseWriter, *http.Request) (int, interface{}, error)
}

func (a AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	encoder := json.NewEncoder(w)
	status, res, err := a.h(w, r)
	if err != nil {
		log.Printf("error: %s", err)
		w.WriteHeader(status)
		encoder.Encode(res)
		return
	}
	w.WriteHeader(status)
	encoder.Encode(res)
	return
}

/*
// GetLoc GetLoc
func (app *App) GetLoc(w http.ResponseWriter, r *http.Request) (int, interface{}, error) {
	res, err := locService(r.Context(), "", time.Now())
	if err != nil {
		app.Logger.Printf("error: %s", err)
		e := ErrorResponse{
			Code:    http.StatusInternalServerError,
			Message: "something went wrong",
		}
		return http.StatusInternalServerError, e, err
	}
	app.Logger.Printf("ok: %v", res)
	return http.StatusOK, res, nil
}
*/

// GetLocWithNumber GetLoc with number
func (app *App) GetLocWithNumber(w http.ResponseWriter, r *http.Request) (int, interface{}, error) {
	val := mux.Vars(r)
	//res, err := locService(r.Context(), val["id"], time.Now())
	res, err := locService(r.Context(), val["id"], time.Now())
	if err != nil {
		app.Logger.Printf("error: %s", err)
		e := ErrorResponse{
			Code:    http.StatusInternalServerError,
			Message: "something went wrong",
		}
		return http.StatusInternalServerError, e, err
	}
	app.Logger.Printf("ok: %v", res)
	return http.StatusOK, res, nil
}

// App application
type App struct {
	Host   string
	Name   string
	Logger *log.Logger
}

func main() {
	host, err := os.Hostname()
	if err != nil {
		log.Fatal(err)
	}

	app := App{
		Name:   "my-service",
		Host:   host,
		Logger: log.New(os.Stdout, fmt.Sprintf("[host=%s] ", host), log.LstdFlags),
	}
	// for gorilla/mux
	router := mux.NewRouter()
	r := router.PathPrefix("/api").Subrouter()
	//r.Methods("GET").Path("/loc").Handler(AppHandler{h: app.GetLoc})
	//r.Methods("GET").Path("/loc/staticName").Handler(AppHandler{h: app.GetLoc})
	r.Methods("GET").Path("/loc/{id}").Handler(AppHandler{h: app.GetLocWithNumber})

	if err := http.ListenAndServe(":8080", router); err != nil {
		log.Fatal(err)
	}
}

クライアント側
main.go で保存して、
>go run main.go
で起動

参考とさせて頂いたページ「Goでhttpリクエストを送信する方法

package main

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

func main() {
	//url := "http://google.co.jp"
	url := "http://localhost:8080/api/loc/2"

	resp, _ := http.Get(url)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(byteArray)) // htmlをstringで取得
}
動作結果
>go run main.go
 {"id":"2","lat":"35.653758","lng":"139.794192"}

JSONで展開するにはどうしたらいいかな?

参考にさせて頂いたのは「goでjson apiを叩く

package main

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

// GetLoc GetLoc
type GetLoc struct {
	ID  string `json:"id"`
	Lat string `json:"lat"`
	Lng string `json:"lng"`
	//Address string  `json:"address"`
}

func main() {
	//url := "http://google.co.jp"
	url := "http://localhost:8080/api/loc/2"

	resp, _ := http.Get(url)
	defer resp.Body.Close()

	/*
		byteArray, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(byteArray)) // htmlをstringで取得
	*/

	var d GetLoc

	fmt.Printf("======Body (use json.Unmarshal)======\n")
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(body, &d)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%v\n", d)

	fmt.Printf("ID:%v\n", d.ID)
	fmt.Printf("Lat:%v\n", d.Lat)
	fmt.Printf("Lng:%v\n", d.Lng)
}

出力結果

>go run main.go
======Body (use json.Unmarshal)======
{2 35.653758 139.794192}
ID:2
Lat:35.653758
Lng:139.794192

2020/09,江端さんの技術メモ

Posted by ebata