LoginSignup
10
6

More than 5 years have passed since last update.

Ubuntu+Golang+MySQLを使用してRESTful APIサーバーを構築する(for Windows x64)

Last updated at Posted at 2018-03-26

概要

Ubuntu(16.04)にGo(1.6)+MySQLの環境を構築し、RESTful APIサーバーを作成した手順を記載します。
RESTful APIサーバー構築部分については、Naren Aryaさんの記事を参考にさせて頂きました。
Ubuntuに環境構築した理由はWindowsマシンに色々と入れたくなかった。。。ただそれだけです。

必要なもの

VMware Workstation Player
 【VMware Workstation 12.5.4 Player for Windows 64-bit Operating Systems.】

Ubuntuのisoイメージ
 【ubuntu-ja-16.04-desktop-amd64.iso】

Postman (※既に使用しているAPIClientToolでも可)
 APIClientツール

Ubuntu(16.04)環境構築

1.VMware Workstation Playerのインストール

ダウンロードした、「VMware-player-12.5.4-5192485.exe」を実行、特に問題が無ければデフォルト値のまま「次へ」を押下し、インストールが完了するまで待ちます。

2.Ubuntuのisoイメージを登録

①「VMware Workstation Player」を起動し、「新規仮想マシンの作成(N)」をクリック
②「インストーラ ディスク イメージ ファイル」にダウンロードした、「ubuntu-ja-16.04-desktop-amd64.iso」を指定
③ユーザー名、パスワード等、任意の文字を指定
④仮想マシン名、任意のフォルダを指定
⑤以降問題が無ければ画面にしたがって進む

3.おまけ(個人的に行ったUbuntuの外観設定)

・画面右上の歯車マーククリック→システム設定→時刻と日付→場所(L):「Tokyo」に変更
・画面右上の歯車マーククリック→システム設定→外観→「Launcherアイコンのサイズ」変更
・画面右上の歯車マーククリック→システム設定→ディスプレイ→「メニューとタイトルバーの拡大縮小」変更
・Launcherを下に配置
 以下のコマンドを「端末(Ctrl+Alt+T)」にて実行

$ gsettings set com.canonical.Unity.Launcher launcher-position Bottom

 左端に戻す場合

$ gsettings set com.canonical.Unity.Launcher launcher-position Left

Golang環境構築(for Ubuntu16.04)

1.Ubuntuアップデート

以下のコマンドを実行

$ sudo apt-get update

2.Goをインストール

以下のコマンドを実行

$ sudo apt-get install golang

バージョンを確認する

$ go version
go version go1.6.2 linux/amd64

3.パスの設定

ホームディレクトリ配下に作業用のフォルダ「go」を作成する。

$ mkdir go

「.bashrc」をnanoエディタにて開く

$ nano ~/.bashrc

「.bashrc」ファイル末尾に下記を追記 ()

export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$PATH
# 保存して閉じる:【Ctrl+O】→【Enter】→【Ctrl+X】

4.Hello World!

ホームディレクトリ配下に作成した「go」フォルダに「hello.go」ファイルを作成

hello.go
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World!")
}

「hello.go」を実行してみる

$ cd $GOPATH
$ go run hello.go
Hello World!

GolangとMySQLを使用して基本的なRESTful APIを構築

1.必要なものを揃える

gitインストール

$ sudo apt-get install git

mysqlServerインストール

$ sudo apt-get install mysql-server
# 今回パスワードは「root」としました。

mysqlドライバーをgithubより取得

$ cd $GOPATH
$ go get "github.com/go-sql-driver/mysql"

APIサーバーを作成するためにGinというHTTPフレームワークを取得

# カレントディレクトリは[$GOPATH]のまま
$ go get "github.com/gin-gonic/gin"

2.MySQLサーバーにデータ登録

MySQLシェルで"gotest"というデータベースを作成

# 下記コマンドでmysqlコマンドラインツール起動
$ mysql -u root -p

# データベース作成
mysql> create database gotest;

# データベースが作成されたか確認
mysql> show create database gotest;
+-----------+--------------------------------------------------------------------+
| Database | Create Database                                                     |
+-----------+--------------------------------------------------------------------+
| WP_DB     | CREATE DATABASE `gotest` /*!40100 DEFAULT CHARACTER SET latin1 */  |
+-----------+--------------------------------------------------------------------+
1 row in set (0.00 sec)

作成した「gotest」DBに「Person Table」を作成
・$GOPATH/src 直下に下記ファイルを作成

migrate_person.go
package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/gotest")
    if err != nil {
        fmt.Println(err.Error())
    }
    defer db.Close()
    // make sure connection is available
    err = db.Ping()
    if err != nil {
        fmt.Println(err.Error())
    }

    stmt, err := db.Prepare("CREATE TABLE person (id int NOT NULL AUTO_INCREMENT, first_name varchar(40), last_name varchar(40), PRIMARY KEY (id));")
    if err != nil {
        fmt.Println(err.Error())
    }
    _, err = stmt.Exec()
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println("Person Table successfully migrated....")
    }
}

・上記「migrate_person.go」実行

$ cd $GOPATH
$ go run migrate_person.go
Person Table successfully migrated....

# MySQLシェルにて対象テーブルが作成されているか確認
mysql> describe person;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| first_name | varchar(40) | YES  |     | NULL    |                |
| last_name  | varchar(40) | YES  |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
3 rows in set (0.02 sec)

3.APIサーバー

・$GOPATH/src 直下に下記ファイルを作成

gitter.go
package main

import (
    "bytes"
    "database/sql"
    "fmt"
    "net/http"

    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/gotest")
    if err != nil {
        fmt.Print(err.Error())
    }
    defer db.Close()
    // make sure connection is available
    err = db.Ping()
    if err != nil {
        fmt.Print(err.Error())
    }
    type Person struct {
        Id         int
        First_Name string
        Last_Name  string
    }
    router := gin.Default()

    // GET a person detail
    router.GET("/person/:id", func(c *gin.Context) {
        var (
            person Person
            result gin.H
        )
        id := c.Param("id")
        row := db.QueryRow("select id, first_name, last_name from person where id = ?;", id)
        err = row.Scan(&person.Id, &person.First_Name, &person.Last_Name)
        if err != nil {
            // If no results send null
            result = gin.H{
                "result": nil,
                "count":  0,
            }
        } else {
            result = gin.H{
                "result": person,
                "count":  1,
            }
        }
        c.JSON(http.StatusOK, result)
    })

    // GET all persons
    router.GET("/persons", func(c *gin.Context) {
        var (
            person  Person
            persons []Person
        )
        rows, err := db.Query("select id, first_name, last_name from person;")
        if err != nil {
            fmt.Print(err.Error())
        }
        for rows.Next() {
            err = rows.Scan(&person.Id, &person.First_Name, &person.Last_Name)
            persons = append(persons, person)
            if err != nil {
                fmt.Print(err.Error())
            }
        }
        defer rows.Close()
        c.JSON(http.StatusOK, gin.H{
            "result": persons,
            "count":  len(persons),
        })
    })

    // POST new person details
    router.POST("/person", func(c *gin.Context) {
        var buffer bytes.Buffer
        first_name := c.PostForm("first_name")
        last_name := c.PostForm("last_name")
        stmt, err := db.Prepare("insert into person (first_name, last_name) values(?,?);")
        if err != nil {
            fmt.Print(err.Error())
        }
        _, err = stmt.Exec(first_name, last_name)

        if err != nil {
            fmt.Print(err.Error())
        }

        // Fastest way to append strings
        buffer.WriteString(first_name)
        buffer.WriteString(" ")
        buffer.WriteString(last_name)
        defer stmt.Close()
        name := buffer.String()
        c.JSON(http.StatusOK, gin.H{
            "message": fmt.Sprintf(" %s successfully created", name),
        })
    })

    // PUT - update a person details
    router.PUT("/person", func(c *gin.Context) {
        var buffer bytes.Buffer
        id := c.Query("id")
        first_name := c.PostForm("first_name")
        last_name := c.PostForm("last_name")
        stmt, err := db.Prepare("update person set first_name= ?, last_name= ? where id= ?;")
        if err != nil {
            fmt.Print(err.Error())
        }
        _, err = stmt.Exec(first_name, last_name, id)
        if err != nil {
            fmt.Print(err.Error())
        }

        // Fastest way to append strings
        buffer.WriteString(first_name)
        buffer.WriteString(" ")
        buffer.WriteString(last_name)
        defer stmt.Close()
        name := buffer.String()
        c.JSON(http.StatusOK, gin.H{
            "message": fmt.Sprintf("Successfully updated to %s", name),
        })
    })

    // Delete resources
    router.DELETE("/person", func(c *gin.Context) {
        id := c.Query("id")
        stmt, err := db.Prepare("delete from person where id= ?;")
        if err != nil {
            fmt.Print(err.Error())
        }
        _, err = stmt.Exec(id)
        if err != nil {
            fmt.Print(err.Error())
        }
        c.JSON(http.StatusOK, gin.H{
            "message": fmt.Sprintf("Successfully deleted user: %s", id),
        })
    })
    router.Run(":3000")
}

・上記「gitter.go」実行

$ cd $GOPATH/src
$ go build gitter.go
$ ./gitter

4.APIClientToolにて確認(Postmanを使用)

【POST】ユーザーを追加 (http:XXX.XXX.XXX.XXX(APIサーバーIPアドレス):3000/person)
image
実行結果
image

【GET】全ユーザーを取得 (http:XXX.XXX.XXX.XXX:3000/persons)
(※ここでは複数ユーザー追加後に実行しています)
image
実行結果
image

【GET】個別にユーザーを取得 (http:XXX.XXX.XXX.XXX:3000/person/"id")
image
実行結果
image

【PUT】ユーザー更新 (http:XXX.XXX.XXX.XXX:3000/person?id="id")
image
実行結果
image

【DELETE】ユーザー削除 (http:XXX.XXX.XXX.XXX:3000/person?id="id")
image
実行結果
image
最終結果
image

最後に

元々、Oracleを使用して、RESTful APIサーバーを構築する予定でしたが、
UbuntuにOracleClinetをインストールした際、うまくいかずUbuntuの再構築をしなければならなくなり、
あきらめてMySQLを使用することにしました。
Golangについてもかなりの初心者ですので、どなたにでもわかり易いように手順だけは細かく記載したつもりですが、
記載に間違いがあれば、ご指摘いただけると助かります。

2017年くらいに勉強用に限定公開していた記事ですが、
今回全体公開としました。バージョンが古かったりと多々問題があるかとは思います。。。

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6