Casual Developers Note

エンジニアやデザイナー向けの技術情報・英語学習情報・海外留学情報・海外旅行情報を提供中。世界を旅して人生を楽しもう。

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • お問い合わせ
現在の場所:ホーム / アーカイブgolang

2019年3月4日 By KD コメントを書く

Go言語のRESTアプリケーションを開発用にオートリロード可能な状態でDocker化する方法(fresh編)

Go言語のRESTアプリケーションを開発用にオートリロード可能な状態でDocker化する方法(fresh編)

Go言語によるRESTアプリケーションの開発を加速するために、Docker化し、freshでオートリロードを可能にする方法を紹介します。

はじめに

以前の記事でGo言語でRESTアプリケーションを実装する方法を紹介しました。今回は、開発環境を簡単に構築できるようにするために、Docker化し、その際に、「fresh」というオートリロード用のモジュールを導入する方法を紹介します。それでは、始めましょう。

前提

以下の準備が完了している必要があります。

  • Go言語がインストールされていること
  • Dockerがインストールされていること
  • 以前の記事で実装したRESTアプリケーションが準備されていること(いつも通りチュートリアル形式で進めていきますが、内容を参考にするだけで良いなら不要です)

詳しくは「環境」を参照してください。

Go言語のRESTアプリケーションを開発用にオートリロード可能な状態でDocker化する

必要なファイルとフォルダを準備する

以前の記事で実装したRESTアプリケーションを準備して、そのプロジェクト内に必要なファイルとフォルダを作成します。

$ cd golang-rest-api/
$ touch Dockerfile.dev
$ touch docker-compose.yml
$ mkdir data
$ touch wait-for-it.sh

今回はローカルにPostgreSQLのデータを残すためにdataフォルダも作成します。また、PostgreSQLのデータベースが起動するのを待つために「wait-for-it」を使います。「wait-for-it.sh」にはGitHub上のスクリプトをコピペしてください。

Dockerファイルを作成する

Dockerファイル内でオートリロードのためのモジュール「fresh」を設定するようにします。

Dockerfile.dev

FROM golang:1.11.4-stretch

LABEL maintainer="YOU"

WORKDIR /app

COPY go.mod .
COPY go.sum .
RUN go mod download
RUN go get github.com/pilu/fresh
RUN go get -u github.com/go-delve/delve/cmd/dlv

COPY . .

EXPOSE 3000

CMD fresh main.go

Docker Composeファイルを作成する

docker-compose.yml

version: '3'

services:
  app:
    container_name: my_app
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app
    depends_on:
      - db
    command: >
      bash -c "chmod +x ./wait-for-it.sh &&
        ./wait-for-it.sh db:5432 -t 30 -- fresh main.go"
  db:
    container_name: my_postgres
    image: postgres:11.1
    restart: always
    environment:
      - "TZ: Etc/UTC"
      - POSTGRES_USER=puser
      - POSTGRES_PASSWORD=ppassword
      - POSTGRES_DB=testdb
    ports:
      - 5432:5432
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./initdb:/docker-entrypoint-initdb.d

RESTアプリケーション内のPostgreSQLの接続先をDocker Composeの設定に合わせて変更する

接続先を「localhost」から「my_postgres」に変更します。

main.go

package main

import (
    "database/sql"
    "golang-rest-api/controllers"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/lib/pq"
)

func main() {
    pgURL, err := pq.ParseURL("postgres://puser:ppassword@my_postgres:5432/testdb?sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }

    db, err := sql.Open("postgres", pgURL)
    if err != nil {
        log.Fatal(err)
    }

    controller := controllers.Controller{}

    router := mux.NewRouter()

    router.HandleFunc("/api/tweets", controller.GetTweets(db)).Methods("GET")
    router.HandleFunc("/api/tweets/{id}", controller.GetTweet(db)).Methods("GET")
    router.HandleFunc("/api/tweets", controller.AddTweet(db)).Methods("POST")
    router.HandleFunc("/api/tweets/{id}", controller.PutTweet(db)).Methods("PUT")
    router.HandleFunc("/api/tweets/{id}", controller.RemoveTweet(db)).Methods("DELETE")

    log.Println("Server up on port 3000...")
    log.Fatal(http.ListenAndServe(":3000", router))
}

動作確認

では動作確認してみましょう。

まずは、Docker化したRESTアプリケーションが正しく動くか確認します。

$ docker-compose up -d
Creating network "golang-rest-api_default" with the default driver
Creating my_postgres ... done
Creating my_app      ... done
$ docker-compose ps
   Name                  Command               State           Ports         
-----------------------------------------------------------------------------
my_app        bash -c chmod +x ./wait-fo ...   Up      0.0.0.0:3000->3000/tcp
my_postgres   docker-entrypoint.sh postgres    Up      0.0.0.0:5432->5432/tcp
$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X POST -d '{"content":"This is my first tweet.","user_name":"@keidrun","comment_num":5,"star_num":15,"re_tweet_num":25}' http://localhost:3000/api/tweets | jq
$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X POST -d '{"content":"Golang is my favorite language!","user_name":"@superdeveloper","comment_num":22,"star_num":222,"re_tweet_num":2222}' http://localhost:3000/api/tweets | jq
$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X POST -d '{"content":"I am nothing. Just an ordinary guy.","user_name":"@person"}' http://localhost:3000/api/tweets | jq
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets | jq
...
[
  {
    "id": 1,
    "content": "This is my first tweet.",
    "user_name": "@keidrun",
    "comment_num": 5,
    "star_num": 15,
    "re_tweet_num": 25
  },
  {
    "id": 2,
    "content": "Golang is my favorite language!",
    "user_name": "@superdeveloper",
    "comment_num": 22,
    "star_num": 222,
    "re_tweet_num": 2222
  },
  {
    "id": 3,
    "content": "I am nothing. Just an ordinary guy.",
    "user_name": "@person",
    "comment_num": 0,
    "star_num": 0,
    "re_tweet_num": 0
  }
]

次に、オートリロードが動作するか確認するために、エラーメッセージを変更してみます。

$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets/1 | jq
...
{
  "id": 1,
  "content": "This is my first tweet.",
  "user_name": "@keidrun",
  "comment_num": 5,
  "star_num": 15,
  "re_tweet_num": 25
}
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets/num | jq
...
{
  "message": "\"id\" is wrong"
}
(ここでソースコード上のエラーメッセージを変更する)
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets/num | jq
...
{
  "message": "\"id\" is absolutely wrong"
}

正しく動くことが確認できました。

おまけ

プロダクション用にDocker化する

Go言語はコンパイル言語なので、プロダクション用にはコンパイルして実行可能な状態にする必要があります。alpineで軽量のDockerイメージにする場合のDockerファイルは以下のようになります。

Dockerfile

FROM golang:1.11.4-stretch AS builder
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server

FROM alpine:3.8
LABEL maintainer="YOU"
WORKDIR /app
EXPOSE 3000
COPY --from=builder /app/server /app/
ENTRYPOINT ["/app/server"]

最後に

いかがでしたか?これでGo言語のRESTアプリケーションをDocker上で開発できるようになったことでしょう。それでは。

環境

  • Go: go1.11.4
  • Docker: 18.09.1, build 4c52b90
  • fresh: v0.0.0-20170301142741-9c0092493eff

カテゴリ : 技術 Tips & Tutorials タグ : auto-reload, docker, fresh, golang, hot-reload, rest-api

2019年2月4日 By KD コメントを書く

Go言語でさくっとREST APIを作ろう(Gorilla Mux編)

Go言語でさくっとREST APIを作ろう(Gorilla Mux編)

Go言語でGorilla Muxというルーティング用のライブラリを使って簡単なCRUDのREST APIを作る方法を紹介します。

はじめに

Go言語にはRubyのRailsやPythonのDjango、JavaScriptのExpressのようなAPIを作る上で絶対的なフレームワークはなく、複数のフレームワークが乱立しているような状態です。例えば、Revel、Gin、Martini、Web.go、Gojiなどがありますが、今回はGorilla web toolkitの中のGorilla Muxというルーティング用のライブラリを使ってAPIを実装することにします。

題材は何でも良いのですが、Twitterっぽいツイートを管理するAPIにしてみましょう。それでは、Go言語で簡単なREST APIを作っていきましょう!

Gorilla Muxとは?

Gorilla Muxは、Gorilla web toolkitというGo言語用のWebツールキット郡の一つで、主にルーティング用のライブラリです。

前提

以下の準備が完了している必要があります。

  • Goがインストールされていること
  • Dockerがインストールされていること

詳しいバージョンは「環境」を参照してください。

PostgreSQLの準備とテーブル作成

テーブル作成用のSQLファイルの準備

今回はデータベースとしてPostgreSQLを使い、Dockerで準備します。Dockerで起動時にテーブルを作成したいので、それ用のSQLファイルを準備します。

$ mkdir initdb
$ touch initdb/1_create_tables.sql

「1_create_tables.sql」は以下のようにします。

create table tweets
(
  id serial primary key,
  content text not null,
  user_name text not null,
  comment_num integer not null default 0,
  star_num integer not null default 0,
  re_tweet_num integer not null default 0
);

「tweets」テーブルを作成するSQLです。

データベースとテーブルの作成

DockerでPostgreSQLを準備し、起動時に先程作成したSQLを読み込ませます。

$ docker run --rm --name my_postgres -p 5432:5432 -e POSTGRES_USER=puser -e POSTGRES_PASSWORD=ppassword -e POSTGRES_DB=testdb -v $PWD/initdb:/docker-entrypoint-initdb.d -d postgres
$ psql -h localhost -p 5432 -U puser -d testdb
Password for user puser:
psql (11.1)
Type "help" for help.

testdb-# \dt
        List of relations
 Schema |  Name  | Type  | Owner
--------+--------+-------+-------
 public | tweets | table | puser
(1 row)

testdb-# \d tweets
                               Table "public.tweets"
    Column    |  Type   | Collation | Nullable |              Default
--------------+---------+-----------+----------+------------------------------------
 id           | integer |           | not null | nextval('tweets_id_seq'::regclass)
 content      | text    |           | not null |
 user_name    | text    |           | not null |
 comment_num  | integer |           | not null | 0
 star_num     | integer |           | not null | 0
 re_tweet_num | integer |           | not null | 0
Indexes:
    "tweets_pkey" PRIMARY KEY, btree (id)

testdb=# \q

うまくテーブル作成まで完了しました。

今回は一時的なデータベースなので、「––rm」オプションを指定しており、停止するとコンテナが削除されます。(データベース及びデータは残りません)また、公開ポートはデフォルトの5432としているので、ローカルでPostgreSQLが起動している場合は、ポート番号を変更してください。

APIの作成

まずは、必要なパッケージやデータベースを準備しましょう。

ベースプロジェクトの作成

プロジェクト用のフォルダを作成し、パッケージをインストールします。それから、今回実装に必要なフォルダとファイルを作成します。

$ mkdir golang-rest-api
$ cd golang-rest-api/
$ go mod init golang-rest-api
$ go get -u github.com/gorilla/mux
$ go get -u github.com/lib/pq
$ go mod graph
golang-rest-api github.com/gorilla/mux@v1.6.2
golang-rest-api github.com/lib/pq@v1.0.0
$ mkdir models
$ mkdir controllers
$ mkdir utils
$ touch main.go
$ touch models/tweet.go
$ touch models/error.go
$ touch controllers/controller.go
$ touch controllers/tweet.go
$ touch utils/respond.go
$ tree
.
├── controllers
│   ├── controller.go
│   └── tweet.go
├── go.mod
├── go.sum
├── main.go
├── models
│   ├── error.go
│   └── tweet.go
└── utils
    └── respond.go

全体像が見えるようになりました。

APIの実装

さくさく実装しましょう。

models/tweet.go

package models

type Tweet struct {
    ID         int    `json:"id"`
    Content    string `json:"content"`
    UserName   string `json:"user_name"`
    CommentNum int    `json:"comment_num"`
    StarNum    int    `json:"star_num"`
    ReTweetNum int    `json:"re_tweet_num"`
}

models/error.go

package models

type Error struct {
    Message string `json:"message"`
}

utils/respond.go

package utils

import (
    "encoding/json"
    "net/http"
)

func Respond(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

controllers/controller.go

package controllers

type Controller struct{}

controllers/tweet.go

package controllers
import (
"database/sql"
"encoding/json"
"golang-rest-api/models"
"golang-rest-api/utils"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
func (c Controller) GetTweets(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var tweet models.Tweet
tweets := make([]models.Tweet, 0)
var errorObj models.Error
rows, err := db.Query("select * from tweets")
if err != nil {
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&tweet.ID, &tweet.Content, &tweet.UserName, &tweet.CommentNum, &tweet.StarNum,
&tweet.ReTweetNum)
if err != nil {
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
tweets = append(tweets, tweet)
}
if err := rows.Err(); err != nil {
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
utils.Respond(w, http.StatusOK, tweets)
}
}
func (c Controller) GetTweet(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var tweet models.Tweet
var errorObj models.Error
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
errorObj.Message = "\"id\" is wrong"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
rows := db.QueryRow("select * from tweets where id=$1", id)
err = rows.Scan(&tweet.ID, &tweet.Content, &tweet.UserName, &tweet.CommentNum, &tweet.StarNum,
&tweet.ReTweetNum)
if err != nil {
if err == sql.ErrNoRows {
errorObj.Message = "The tweet does not exist"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
utils.Respond(w, http.StatusOK, tweet)
}
}
func (c Controller) AddTweet(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var tweet models.Tweet
var errorObj models.Error
json.NewDecoder(r.Body).Decode(&tweet)
if tweet.Content == "" {
errorObj.Message = "\"content\" is missing"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
if tweet.UserName == "" {
errorObj.Message = "\"user_name\" is missing"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
if tweet.CommentNum < 0 {
errorObj.Message = "\"comment_num\" must be greater than or equal to 0"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
if tweet.StarNum < 0 {
errorObj.Message = "\"star_num\" must be greater than or equal to 0"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
if tweet.ReTweetNum < 0 {
errorObj.Message = "\"re_tweet_num\" must be greater than or equal to 0"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
err := db.QueryRow(
"insert into tweets (content, user_name, comment_num, star_num, re_tweet_num)"+
" values($1, $2, $3, $4, $5) RETURNING id;",
tweet.Content, tweet.UserName, tweet.CommentNum, tweet.StarNum, tweet.ReTweetNum).Scan(&tweet.ID)
if err != nil {
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
utils.Respond(w, http.StatusCreated, tweet)
}
}
func (c Controller) PutTweet(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var tweet models.Tweet
var errorObj models.Error
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
errorObj.Message = "\"id\" is wrong"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
json.NewDecoder(r.Body).Decode(&tweet)
if tweet.CommentNum < 0 {
errorObj.Message = "\"comment_num\" must be greater than or equal to 0"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
if tweet.StarNum < 0 {
errorObj.Message = "\"star_num\" must be greater than or equal to 0"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
if tweet.ReTweetNum < 0 {
errorObj.Message = "\"re_tweet_num\" must be greater than or equal to 0"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
var update models.Tweet
rows := db.QueryRow("select * from tweets where id=$1", id)
err = rows.Scan(&update.ID, &update.Content, &update.UserName, &update.CommentNum,
&update.StarNum, &update.ReTweetNum)
if err != nil {
if err == sql.ErrNoRows {
errorObj.Message = "The tweet does not exist"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
if tweet.Content != "" {
update.Content = tweet.Content
}
if tweet.UserName != "" {
update.UserName = tweet.UserName
}
if tweet.CommentNum > 0 {
update.CommentNum = tweet.CommentNum
}
if tweet.StarNum > 0 {
update.StarNum = tweet.StarNum
}
if tweet.ReTweetNum > 0 {
update.ReTweetNum = tweet.ReTweetNum
}
result, err := db.Exec(
"update tweets set content=$1, user_name=$2, comment_num=$3, star_num=$4, re_tweet_num=$5"+
" where id=$6 RETURNING id",
&update.Content, &update.UserName, &update.CommentNum, &update.StarNum, &update.ReTweetNum, id)
if err != nil {
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
_, err = result.RowsAffected()
if err != nil {
if err == sql.ErrNoRows {
errorObj.Message = "The tweet does not exist"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
utils.Respond(w, http.StatusOK, update)
}
}
func (c Controller) RemoveTweet(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var errorObj models.Error
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
errorObj.Message = "\"id\" is wrong"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
result, err := db.Exec("delete from tweets where id = $1", id)
if err != nil {
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
_, err = result.RowsAffected()
if err != nil {
if err == sql.ErrNoRows {
errorObj.Message = "The tweet does not exist"
utils.Respond(w, http.StatusBadRequest, errorObj)
return
}
log.Println(err)
errorObj.Message = "Server error"
utils.Respond(w, http.StatusInternalServerError, errorObj)
return
}
utils.Respond(w, http.StatusNoContent, "")
}
}

main.go

package main
import (
"database/sql"
"golang-rest-api/controllers"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/lib/pq"
)
func main() {
pgURL, err := pq.ParseURL("postgres://puser:ppassword@localhost:5432/testdb?sslmode=disable")
if err != nil {
log.Fatal(err)
}
db, err := sql.Open("postgres", pgURL)
if err != nil {
log.Fatal(err)
}
controller := controllers.Controller{}
router := mux.NewRouter()
router.HandleFunc("/api/tweets", controller.GetTweets(db)).Methods("GET")
router.HandleFunc("/api/tweets/{id}", controller.GetTweet(db)).Methods("GET")
router.HandleFunc("/api/tweets", controller.AddTweet(db)).Methods("POST")
router.HandleFunc("/api/tweets/{id}", controller.PutTweet(db)).Methods("PUT")
router.HandleFunc("/api/tweets/{id}", controller.RemoveTweet(db)).Methods("DELETE")
log.Println("Server up on port 3000...")
log.Fatal(http.ListenAndServe(":3000", router))
}

これで実装完了です。ブログ記事にする関係でモジュール化を控えめにしているので、少々長いコードになっていますがご了承ください(笑)

動作確認

最後に作ったAPIの動作確認をしてみましょう。

APIを起動します。

$ go run main.go
2019/01/20 21:47:03 Server up on port 3000...

curlでCRUDを確認します。

$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X POST -d '{"content":"This is my first tweet.","user_name":"@keidrun","comment_num":5,"star_num":15,"re_tweet_num":25}' http://localhost:3000/api/tweets | jq
...
< HTTP/1.1 201 Created
...
{
"id": 1,
"content": "This is my first tweet.",
"user_name": "@keidrun",
"comment_num": 5,
"star_num": 15,
"re_tweet_num": 25
}
$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X POST -d '{"content":"Golang is my favorite language!","user_name":"@superdeveloper","comment_num":22,"star_num":222,"re_tweet_num":2222}' http://localhost:3000/api/tweets | jq
...
< HTTP/1.1 201 Created
...
{
"id": 2,
"content": "Golang is my favorite language!",
"user_name": "@superdeveloper",
"comment_num": 22,
"star_num": 222,
"re_tweet_num": 2222
}
$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X POST -d '{"content":"I am nothing. Just an ordinary guy.","user_name":"@person"}' http://localhost:3000/api/tweets | jq
...
< HTTP/1.1 201 Created
...
{
"id": 3,
"content": "I am nothing. Just an ordinary guy.",
"user_name": "@person",
"comment_num": 0,
"star_num": 0,
"re_tweet_num": 0
}
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets | jq
...
< HTTP/1.1 200 OK
...
[
{
"id": 1,
"content": "This is my first tweet.",
"user_name": "@keidrun",
"comment_num": 5,
"star_num": 15,
"re_tweet_num": 25
},
{
"id": 2,
"content": "Golang is my favorite language!",
"user_name": "@superdeveloper",
"comment_num": 22,
"star_num": 222,
"re_tweet_num": 2222
},
{
"id": 3,
"content": "I am nothing. Just an ordinary guy.",
"user_name": "@person",
"comment_num": 0,
"star_num": 0,
"re_tweet_num": 0
}
]
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets/1 | jq
...
< HTTP/1.1 200 OK
...
{
"id": 1,
"content": "This is my first tweet.",
"user_name": "@keidrun",
"comment_num": 5,
"star_num": 15,
"re_tweet_num": 25
}
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets/2 | jq
...
< HTTP/1.1 200 OK
...
{
"id": 2,
"content": "Golang is my favorite language!",
"user_name": "@superdeveloper",
"comment_num": 22,
"star_num": 222,
"re_tweet_num": 2222
}
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets/3 | jq
...
< HTTP/1.1 200 OK
...
{
"id": 3,
"content": "I am nothing. Just an ordinary guy.",
"user_name": "@person",
"comment_num": 0,
"star_num": 0,
"re_tweet_num": 0
}
$ curl -v -H "Accept:application/json" -H "Content-Type:application/json" -X PUT -d '{"content":"I am excellent guy!!","user_name":"@awesomeperson","comment_num":99,"star_num":999,"re_tweet_num":9999}' http://localhost:3000/api/tweets/3 | jq
...
< HTTP/1.1 200 OK
...
{
"id": 3,
"content": "I am excellent guy!!",
"user_name": "@awesomeperson",
"comment_num": 99,
"star_num": 999,
"re_tweet_num": 9999
}
$ curl -v -H "Accept:application/json" -X DELETE http://localhost:3000/api/tweets/2 | jq
...
< HTTP/1.1 204 No Content
...
$ curl -v -H "Accept:application/json" http://localhost:3000/api/tweets | jq
...
< HTTP/1.1 200 OK
...
[
{
"id": 1,
"content": "This is my first tweet.",
"user_name": "@keidrun",
"comment_num": 5,
"star_num": 15,
"re_tweet_num": 25
},
{
"id": 3,
"content": "I am excellent guy!!",
"user_name": "@awesomeperson",
"comment_num": 99,
"star_num": 999,
"re_tweet_num": 9999
}
]

大丈夫そうですね。これで動作確認は完了です。

最後に

いかがでしたか?これでGo言語でGorilla Muxを使ってAPIを作成できるようになったと思います。では。

環境

  • Go: 1.11.4
  • mux: 1.6.2
  • pq: 1.6.2
  • Docker: 18.09.1, build 4c52b90

カテゴリ : 技術 Tips & Tutorials タグ : golang, gorilla-mux, rest-api

2019年1月28日 By KD コメントを書く

Go言語のためのVisual Studio Codeの設定方法

Go言語のためのVisual Studio Codeの設定方法

今後流行るプログラミング言語の筆頭はGo言語です。今回は、Visual Studio CodeのGo言語のための設定方法を紹介します。

はじめに

Visual Studio Codeは人気のGo言語においても十分なコーディング環境が実現できます。しかもプラグインを入れるだけです。それでは、Go言語用の設定方法を紹介します

Visual Studio CodeのGo用設定

前提

以下が完了している必要があります。

  • Golangがインストール済みであること
  • Visual Studio Codeがインストール済みであること
  • Visual Studio Codeの基本的な設定が完了していること

Visual Studio Codeの基本設定は以前の記事を参考にしてください。

プラグインをインストールする

Visual Studio CodeでGoを扱うためにはGo用のプラグインを入れる必要があります。

NewImage

これでGoファイルを開くと以下のようにダイアログが表示され、「Install」をクリックすることで必要なモジュールがインストールされます。

NewImage

NewImage

「GOPATH」はデフォルトで「~/go」になります。

自動フォーマットのための設定

Visual Studio CodeでGoの2種類の設定方法を紹介します。一つは、よくあるファイルの保存時に自動的にフォーマットする設定方法で、もう一つは、ファイルを自動的に保存する設定方法です。

自動フォーマットにする設定は以下です。

{
...
"[go]": {
"editor.tabSize": 4,
"editor.insertSpaces": false,
"editor.formatOnSave": true,
"editor.formatOnPaste": false,
"editor.formatOnType": false
},
"go.formatTool": "goreturns",
"go.formatFlags": [
"-w"
],
"go.lintTool": "golint",
"go.lintOnSave": "package",
"go.vetOnSave": "package",
"go.buildOnSave": "package",
"go.testOnSave": false,
"go.gocodeAutoBuild": true,
"go.installDependenciesWhenBuilding": true,
...
}

設定可能なフォーマッタは「gofmt」、「goimports」、「goreturns」、「goformat」になります。

自動保存のための設定

Goはコンパイル言語のため、ファイルが保存されてコンパイルされた後でないと正しく動作しません。そのため、自動フォーマットの代わりに自動保存の設定をする場合があります。

自動保存の設定は以下です。

{
...
"[go]": {
"editor.tabSize": 4,
"editor.insertSpaces": false,
"editor.formatOnSave": false,
"editor.formatOnPaste": false,
"editor.formatOnType": false
},
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000,
"go.formatTool": "goreturns",
"go.formatFlags": [
"-w"
],
"go.lintTool": "golint",
"go.lintOnSave": "package",
"go.vetOnSave": "package",
"go.buildOnSave": "package",
"go.testOnSave": false,
"go.gocodeAutoBuild": true,
"go.installDependenciesWhenBuilding": true,
...
}

詳しくは公式ドキュメントを参照してください。

おまけ

Go用のプラグインのエラーの対処法

MacでGo用のプラグインを使う場合に以下のエラーが発生することがあります。

「could not launch process: debugserver or lldb-server not found: install XCode’s command line tools or lldb-server」

これは、以下を実行することで解決できます。

$ xcode-select --install

デバッグ用のライブラリのインストール

Go言語をデバッグするには「delve」というデバッガーをインストールする必要があります。

VSCode上からコマンドパレッドで以下のように検索します。

NewImage

「dlv」にチェックを入れて「OK」をクリックすればローカルにインストールされます。

NewImage

あるいは、以下のようにコマンドラインからインストールすることも可能です。

$ go get -u github.com/go-delve/delve/cmd/dlv

デバッグ方法は公式ドキュメントを参照してください。

最後に

いかがでしたか?これでVisual Studio CodeのGo設定ができたと思います。Go言語のコーディングを楽しみましょう。それでは。

環境

  • PC: macOS Mojava 10.14.2
  • Visual Studio Code: 1.30.1
  • Go: 1.11.4

カテゴリ : 技術 Tips & Tutorials タグ : golang, vscode

ブログ更新情報や海外の関連情報などを配信する無料メルマガ

Sponsored Links

About Author

KD

世界を旅し日本を愛するエンジニア。大学でコンピュータサイエンスの楽しさを学び、日本の大手IT企業で働く中で、新しい技術やスケールするビジネスが北米にある事に気づく。世界に挑戦するための最大の壁が英語であったため、フィリピン留学およびカナダ留学を経て英語を上達させた。現在は日本在住でエンジニアとして働きつつ、次の挑戦に備えて世界の動向を注視している。挑戦に終わりはない。このブログでは、エンジニアやデザイナー向けの技術情報から、海外に留学したい人向けの留学情報、海外に興味がある人向けの海外旅行情報など、有益な情報を提供しています。

https://casualdevelopers.com/

最近の投稿

  • 2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年JS周辺のバックエンド寄りの注目技術!ネクストNodeJSの「Deno」と分散型パッケージレジストリの「Entropic」の紹介

    2020年1月13日
  • 今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    今さら聞けないJavaによる関数型プログラミング入門 ~ラムダ式、ストリーム、関数型インターフェース~

    2019年11月4日
  • ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    ReactのためのEslintおよびPrettierの設定方法 ~Airbnb JavaScript Style Guideの適用~

    2019年10月30日
  • BashからZshに移行する方法(Mac編)

    BashからZshに移行する方法(Mac編)

    2019年10月21日
  • Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    Create React Appを使わないでゼロからReactの開発環境を構築する方法(Webpack/Docker編)

    2019年9月30日

カテゴリ

  • 技術 Tips & Tutorials (100)
  • 技術塾 (6)
  • ライフハック (26)
  • 海外留学 (12)
  • 英語学習 (3)
  • コラム (6)

アーカイブ

最高の学習のために

人気記事ランキング

  • MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
    MySQLで「ERROR 2003 (HY000): Can't connect to MySQL server」と怒られた時の対処法
  • Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
    Jupyter Notebookで「The kernel appears to have died. It will restart automatically.」というエラーが出た場合の原因と対処法
  • Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
    Expressで「Cannot set headers after they are sent to the client」と怒られた時の対処法
  • 爆速でJenkinsをマスターしよう(GitHubアカウント統合編) ~ JenkinsのGitHub Organizationの設定方法 ~
    爆速でJenkinsをマスターしよう(GitHubアカウント統合編) ~ JenkinsのGitHub Organizationの設定方法 ~
  • SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
    SAKURAのメールボックスで独自ドメインのメールを設定し、Gmail経由で送受信する方法
  • バンクーバー留学豆知識:バンクーバーのATMで日本の銀行のキャッシュカードを使ってお得にお金を引き出す方法
    バンクーバー留学豆知識:バンクーバーのATMで日本の銀行のキャッシュカードを使ってお得にお金を引き出す方法
  • [tips][perl] Perlで文字コードをいい感じに処理する方法
    [tips][perl] Perlで文字コードをいい感じに処理する方法
  • PythonでWebスクレイピング入門(Scrapy+Selenium編)
    PythonでWebスクレイピング入門(Scrapy+Selenium編)
  • Amazon EC2インスタンスにSSHできなくなった時の対処法
    Amazon EC2インスタンスにSSHできなくなった時の対処法
  • SpringBootのProfile毎にプロパティを使い分ける3つの方法
    SpringBootのProfile毎にプロパティを使い分ける3つの方法

Bitcoin寄付 / BTC Donation

Bitcoinを寄付しよう

BTC
Select Payment Method
Personal Info

Donation Total: BTC 0.0010

このブログの運営のためにBitcoinでの寄付を募集しています。お気持ち程度の寄付を頂けると管理者の励みになります。

Bitcoin寄付について知りたい方はこちらの記事へ

ビットコイン取引ならここ

  • ホーム
  • 技術 Tips & Tutorials
  • 技術塾
  • ライフハック
  • 海外留学
  • 英語学習
  • コラム
  • サイトマップ
  • タグ一覧
  • プライバシーポリシー
  • お問い合わせ

Copyright © 2023 KD - Casual Developers Notes