今回は、Webフレームワーク①Echo
と②Ent.
を使って簡単なWebアプリを作ってみます。
こちらの記事を参考にしました↓
Echo公式サイト
ent.公式サイト
今回の記事では少し変更を加えています。
ディレクトリ構成
MYECHO
├─ ent
│ ├─ entry
│ ├─ enttest
│ ├─ hook
│ ├─ migrate
│ ├─ prodicate
│ ├─ runtime
│ ├─ schema
│ │ └─ entry.go
│ ├─ client.go
│ ├─ ent.go
│ ├─ entry_create.go
│ ├─ entry_delete.go
│ ├─ entry_query.go
│ ├─ entry_update.go
│ ├─ entry.go
│ ├─ ganerate.go
│ ├─ mutation.go
│ ├─ runtime.go
│ └─ tx.go
├─ templates
│ └─ index.htm
├─ go.mod
├─ go.sum
└─ main.go
サンプル
http://localhost:8989/
にアクセスしてタスク投稿画面を起動します。
タスクが無事登録されると、下記のキャプチャのように登録内容が表示されます。
変更点
■CSSファイルを読み込む設定を追加
■CSSフレームワークBootstrapを使用
■PostgreSQLを使用
Echoプロジェクト
を作成する
【手順】
■デスクトップにプロジェクトフォルダを作る
mkdir <プロジェクト名>
(例)
mkdir myEcho
■Echo公式サイトの通りモジュールを初期化する。
go mod init <モジュール名>
(例)
go mod init myapp
■Echo公式さとでEchoプロジェクトに必要なソースをインストールする。
go get github.com/labstack/echo/v4
■Echoプロジェクト直下に「main.go」を追加する
■プロジェクトフォルダ(例:myEchoフォルダ)に移動して、entスキーマ
を作成する。
go run -mod=mod entgo.io/ent/cmd/ent new <スキーマ名>
(例)
go run -mod=mod entgo.io/ent/cmd/ent new Entry
■スキーマを定義する。
package schema
import (
"time"
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Entry holds the schema definition for the Entry entity.
type Entry struct {
ent.Schema
}
// Fields of the Entry.
func (Entry) Fields() []ent.Field { //←の関数を修正する
return []ent.Field{
field.String("content").Default(""),
field.Time("created_at").Default(func() time.Time { return time.Now() }),
}
}
// Edges of the Entry.
func (Entry) Edges() []ent.Edge {
return nil
}
■修正したスキーマをもとにクラスを自動酢生成する。
go generate ./ent
すると、下記のようにフォルダが自動で生成される。
■PostgreSQLドライバ
をインストールする。
go get github.com/lib/pq
■モジュールの依存関係を解消する。
go mod tidy
■Echo
の最新モジュールをインストールする。
go get github.com/labstack/echo/v4
■main.go
を修正する。
package main
import (
"context"
"html/template"
"io"
"log"
"myapp/ent"
"myapp/ent/entry"
"net/http"
"github.com/labstack/echo/v4"
_ "github.com/lib/pq"
)
// CustomRenderer is a custom renderer for HTML templates.
type CustomRenderer struct {
templates *template.Template
}
// NewCustomRenderer creates a new CustomRenderer.
func NewCustomRenderer() *CustomRenderer {
// Load all templates from the templates directory
tmpl, err := template.ParseFiles("templates/index.html")
if err != nil {
log.Fatal("Error loading template:", err)
}
return &CustomRenderer{templates: tmpl}
}
// Render renders the template with the given name and data.
func (cr *CustomRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
// Render the template to the provided io.Writer (which is http.ResponseWriter)
return cr.templates.ExecuteTemplate(w, name, data)
}
func main() {
client, err := ent.Open("postgres", "host=<ホスト名> port=5432 user=<ユーザ名> dbname=<データベース名> password=<パスワード> sslmode=disable")
if err != nil {
log.Fatalf("failed opening connection to postgres: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
e := echo.New()
// Set the custom renderer for rendering HTML templates
e.Renderer = NewCustomRenderer()
//スタイリングシートCSS読み込み設定
e.Static("/css", "src/css")
//Bootstrap読み込み設定
e.Static("/bootstrap/css", "src/lib/bootstrap/css")
e.GET("/", func(c echo.Context) error {
// Fetch the 10 most recent entries
eq := client.Entry.Query().Order(ent.Desc(entry.FieldCreatedAt)).Limit(10)
entries, err := eq.All(context.Background())
if err != nil {
log.Println("Error fetching entries:", err)
return c.String(http.StatusInternalServerError, "Failed to load entries")
}
// Render the index.html template with the entries
return c.Render(http.StatusOK, "index.html", map[string]interface{}{
"entries": entries,
})
})
e.POST("/add", func(c echo.Context) error {
e := client.Entry.Create()
e.SetContent(c.FormValue("content"))
if _, err := e.Save(context.Background()); err != nil {
log.Println(err.Error())
return c.String(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
return c.Redirect(http.StatusFound, "/")
})
e.Logger.Fatal(e.Start(":8989"))
}
エラー「failed creating schema resources: querying server version pq: SSL is not enabled on the server」が出た場合の対処方法
このエラーは、PostgreSQLサーバーがSSL接続を要求しているにもかかわらず、entライブラリでSSLが無効の設定で接続しようとしているため発生しています。
エラーメッセージによれば、「SSLが有効でないサーバーに接続しようとしている」とあります。
解決方法
SSLを無効にする: PostgreSQLサーバーがSSLを要求していない場合でも、接続文字列の中にSSLを無効にする設定を追加することで解決できます。
SSLを有効にする: PostgreSQLサーバーがSSLを要求している場合、entライブラリで接続時にSSLを有効にする必要があります。
1. SSL無効にする場合
接続文字列にsslmode=disable
を追加して、SSLを無効にすることができます。以下のように修正します。
client, err := ent.Open("postgres", "host=<ホスト名> port=5432 user=<ユーザ名> dbname=<データベース名> password=<パスワード> sslmode=disable")
2. SSL有効にする場合
PostgreSQLサーバーがSSL接続を要求している場合、接続文字列に適切なSSLオプションを追加する必要があります。例えば、sslmode=require
を指定します。
client, err := ent.Open("postgres", "host=<ホスト名> port=5432 user=<ユーザ名> dbname=<データベース名> password=<パスワード> sslmode=require")
テンプレートエンジンhtmlを使用する場合
GoのEchoフレームワークでHTMLテンプレートをレンダリングするためには、echo.Rendererインターフェースを実装する必要があります。
以下の手順で修正できます:
テンプレートレンダラーの作成: Echoフレームワークでは、HTMLテンプレートをレンダリングするために、echo.Renderer
インターフェースを実装したカスタムレンダラーを作成する必要があります。
テンプレートファイルのレンダリング: Echoフレームワークはhtml/template
を使ってテンプレートをレンダリングできます。t.Executeの代わりに、c.Render
メソッドを使ってレンダリングします。
使用する関数としてはRender(w io.Writer, name string, data interface{}, c echo.Context)
を使用してレンダリングを行います。
c.Render
で渡すテンプレート名は、ファイル名(拡張子なし)で指定します。templates/index.htmlを指定している部分を、ファイル名index.html
を渡すようにします。
■Echoプロジェクトを起動する。
go run main.go
そうすると、PostgreSQLのテーブルにスキーマが反映されます。
スタイリングシートの読み込み
CSSやBootstrap,Javascriptなどのファイルを読み込んでみましょう。
//スタイリングシートCSS読み込み設定
e.Static("/css", "src/css")
//Bootstrap読み込み設定
e.Static("/bootstrap/css", "src/lib/bootstrap/css")
第一引数に省略した形のパスを、第二引数に相対パスを記載します。
つぎにHTMLファイルでは下記のように記載します。
href
の部分は、前述の関数(e.Static("第一引数","第二引数")
)の第一引数と読み込むファイル名を記載します。
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/index.css"/>
全体のコードはこちら↓
<!DOCTYPE html>
<html>
<head>
<title>タイトル</title>
<!--共通-->
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<!--個別ファイル-->
<link rel="stylesheet" href="css/index.css"/>
</head>
<body>
<h1>投稿内容</h1>
<form method="post" action="/add">
<label for="exampleInputEmail1" class="form-label">登録するタスク</label>
<input type="text" name="content" class="form-control border border-primary" required/>
<input type="submit" value="送信" class="btn btn-primary"/>
</form>
<ul>
{{range .entries}}
<li>{{.Content}}</li>
{{else}}
<li>まだ投稿はありません。</li>
{{end}}
</ul>
</body>
</html>
以上です。
参考にしたサイト