サンプル
画面でユーザ情報を入力して送信ボタンをクリックします。
データベースに登録されるとEmail
をキーにして単一のユーザ情報を画面側に返却します。
PostgreSQLを確認するとちゃんと登録されています。
このような画面を作っていきます。
この記事で会う使う技術
技術 | パッケージ等 |
---|---|
トランザクション | − |
ORMマッパー | − |
パスワードハッシュ化 | golang.org/x/crypto/bcrypt |
パスワードハッシュ化に関するパッケージはこちら↓
Beegoのバージョン
Beego | バージョン |
---|---|
Beego | 2.0 |
ディレクトリ構造
パスワードをハッシュ化するクラスcrypt.go
を追加しました。
そのほかに、todo.go
クラスやdefault.go
クラスも変更しました。
詳しくは後述します。
My-First-Beego-Project
├─ conf
│ └─ app.conf
├─ controllers
│ └─ default.go
├─ crypto
│ └─ crypt.go
├─ models
│ └─ todo.go
└─ routers
└─ router.go
static
├─ css
├─ img
├─ js
│ ├─ form.js
│ └─ reload.min.js
├─ lib
│ └─ boostrap
│ ├─ css
│ │ ├─ bootstrap-grid.min.css
│ │ └─ bootstrap.min.css
│ └─ js
│ ├─ bootstrap-bundle.min.js
│ └─ bootstrap.min.js
├─ jquery
│ └─ jquery.js
├─ views
│ ├─ index.tpl
│ ├─ subIndex.tpl
│ └─ thirdIndex.tpl
└─ main.go
フロント側の画面
こちらは特に変えていませんが、載せておきます。
<!DOCTYPE>
<html>
<head>
<title>3つめのページ</title>
<!--共通ファイル-->
<link rel="stylesheet" type="text/css" href="/static/lib/bootstrap/css/bootstrap.min.css"/>
<script type="text/javascript" src="/static/lib/jquery/jquery.js"></script>
<script type="text/javascript" src="/static/js/form.js"></script>
</head>
<body>
<h2>取得したURLクエリパラメータは:{{.ID}}</h2>
<form>
<div class="form-group">
<label>名前</label>
<input type="text" id="name" name="name" class="form-control border border-dark" required/>
</div>
<div class="form-group">
<label>E-mail</label>
<input type="email" id="email" name="email" class="form-control border border-dark" required/>
</div>
<div class="form-group">
<label>パスワード</label>
<input type="password" id="password" name="password" class="form-control border border-dark" required/>
</div>
<div class="mt-4">
<button type="button" id="submitBtn" class="btn btn-primary">
送信
</button>
</div>
</form>
<div>
送信したデータ
<div id="requestedName"></div>
<div id="requestedEmail"></div>
<div id="requestedPassword"></div>
</div>
</body>
</html>
サーバ側の画面
パスワードをハッシュするためのパッケージgolang.org/x/crypto/bcrypt
をインストールしておきます。
go get golang.org/x/crypto/bcrypt
インストールしたら、crypt.go
クラスを作成しておきます。
package crypto
import (
"golang.org/x/crypto/bcrypt"
)
// パスワードのハッシュ化処理
func GeneratedPasswordHashed(password string) (string, error) {
hash, error := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(hash), error
}
// 入力されたパスワードとデータベースのパスワードの比較
func ComparedHashedPassword(hasedPassword, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hasedPassword), []byte(password))
}
次にモデルクラスです。
package models // ここでパッケージ名を指定
import (
"time"
"github.com/beego/beego/v2/client/orm"
_ "github.com/lib/pq" // PostgreSQL ドライバをインポート
)
type Register struct {
Id int64 `orm:"auto"`
Name string `orm:"size(20)"`
Email string `orm:"size(50)"`
Password string `orm:"size(100)"`
CreatedAt time.Time `orm:"auto_now;type(datetime)"`
}
func init() {
// PostgreSQLのドライバ登録
orm.RegisterDriver("postgres", orm.DRPostgres)
// データベース接続設定
orm.RegisterDataBase("default", "postgres", "user=postgres password=postgres host=localhost port=5432 dbname=beegoDb sslmode=disable")
// モデルの登録
orm.RegisterModel(new(Register)) // Todo
// テーブルが存在しない場合に作成(強制的にマイグレーション)
orm.RunSyncdb("default", false, true)
}
さいごに、default.go
を変更します。
package controllers
import (
"fmt"
"time"
"my-first-beego-project/models"
_ "my-first-beego-project/models"
"github.com/astaxie/beego"
"github.com/beego/beego/v2/client/orm"
"my-first-beego-project/crypto"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
c.Data["Website"] = "beego.me"
c.Data["Email"] = "astaxie@gmail.com"
c.TplName = "index.tpl"
}
// 20250205追加
type SubController struct {
beego.Controller
}
type ThirdController struct {
beego.Controller
}
type UserController struct {
beego.Controller
}
// 20250205
func (c *SubController) Get() {
c.Data["Page"] = "SubPage"
c.TplName = "subIndex.tpl"
}
// @router :id[get]
func (this *ThirdController) Get() {
this.Data["ID"] = this.Ctx.Input.Param(":id")
this.TplName = "thirdIndex.tpl"
}
func (c *UserController) Post() {
//フォームデータを取得する
name := c.GetString("name")
email := c.GetString("email")
password := c.GetString("password")
//デバッグ用
//fmt.Println("フォーム画面から取得したNameは:", name)
//fmt.Println("フォーム画面から取得したEmailは:", email)
//fmt.Println("フォーム画面から取得したPasswordは:", password)
c.Data["json"] = map[string]interface{}{
"status": "成功",
"message": "フォーム画面からデータを受け取りました。",
"requestData": map[string]string{
"Name": name,
"Email": email,
"Password": password,
},
}
/*
c.Data["json"] = map[string]string{
"status":"成功",
"message":"フォーム画面からデータを受け取りました。",
}
*/
//パスワードのハッシュ化
generatedHashedPassword, error := crypto.GeneratedPasswordHashed(password)
if error != nil {
fmt.Println("パスワードを暗号化中にエラーが発生しました。", error)
c.Data["json"] = map[string]string{
"status": "失敗",
"message": "パスワードのハッシュ化に失敗しました。",
}
c.ServeJSON()
return
}
//ORMを取得する
o := orm.NewOrm()
//モデルTodoのインスタンスを生成
var registerUser models.Register //var todo models.Todo
//トランザクション開始
tx, err := o.Begin()
if err != nil {
fmt.Println("トランザクション開始エラー", err)
c.Data["json"] = map[string]string{
"status": "失敗",
"message": "トランザクションの開始に失敗しました。",
}
}
registerUser.Name = name
registerUser.Email = email
registerUser.Password = generatedHashedPassword
registerUser.CreatedAt = time.Now()
_, err = tx.Insert(®isterUser)
if err != nil {
tx.Rollback()
fmt.Println("エラー発生", err)
c.Data["json"] = map[string]string{
"status": "失敗",
"message": "ユーザ登録に失敗しました。",
}
c.ServeJSON()
return
}
//成功した場合コミット
err = tx.Commit()
if err != nil {
fmt.Println("トランザクションのコミットに失敗しました。", err)
c.Data["json"] = map[string]string{
"status": "失敗",
"message": "トランザクションのコミットに失敗しました。",
}
c.ServeJSON()
return
}
//登録したデータを取得する
var registeredUser models.Register
err = o.QueryTable("register").Filter("Email", email).One(®isteredUser)
if err != nil {
fmt.Println("ユーザ情報の取得に失敗しました。", err)
c.Data["json"] = map[string]string{
"status": "失敗",
"message": "ユーザー情報の取得に失敗しました。",
}
c.ServeJSON()
return
}
fmt.Println("登録されたユーザは、", registeredUser.Name)
fmt.Println("登録されたEmailは、", registeredUser.Email)
//成功時のレスポンス
c.Data["json"] = map[string]interface{}{
"status": "成功",
"message": "ユーザーが正常に登録されました",
"requestData": map[string]string{
"Name": registeredUser.Name,
"Email": registeredUser.Email,
},
}
//JSONデータを返却する
c.ServeJSON()
}
Beego ver2.0
でのトランザクションの方法
Beego ver 2.0
でトランザクションをする方法は下記の手順になります。
o := orm.NewOrm()
でOrmオブジェクトを作成する
o := orm.NewOrm()
tx,err := o.Begin()
でトランザクションを開始する
Beego v2のORM(beego/v2)では、トランザクション管理は少し異なり、o.Begin()を使う代わりにo.Begin()で返されたtx(トランザクション)を使います。
tx
を使ってInsert、Commit、Rollbackなどの操作を行います。
tx, err := o.Begin()
DBに挿入したいModel
インスタンスを作成する
var registerUser models.Register
データ更新が失敗した場合ロールバックを行う
_, err = tx.Insert(®isterUser)
if err != nil {
tx.Rollback()
fmt.Println("エラー発生", err)
c.Data["json"] = map[string]string{
"status": "失敗",
"message": "ユーザ登録に失敗しました。",
}
c.ServeJSON()
return
}
データ更新成功後はコミットする
err = tx.Commit()
登録したユーザ情報を画面側に渡す方法
o.QueryTable("モデル名").Filter("カラム名", カラム値).One(&モデルポインタ)
を使ってデータを取得します。
o.QueryTable("モデル名").Filter("カラム名", カラム値).One(&モデルポインタ)
の引数として ポインタ型で渡します。
そのため、今回はOne() メソッドは、データを構造体にマッピングするためにポインタを必要とします。
ですので、®isteredUser とする必要があります。
変数errがQueryTableの実行結果でエラーが発生した場合、err != nilが評価され、そのエラーメッセージが表示されます。
//登録したデータを取得する
var registeredUser models.Register
err = o.QueryTable("register").Filter("Email", email).One(®isteredUser)
この場合は、email
をキーにしてデータベースから登録したユーザーの情報を取得します。
参考にしたサイト