[Go言語] revelのexamplesのchatを覗いてみる

examplesのgit: https://github.com/revel/examples

$ git clone https://github.com/revel/examples.git
$ revel run github.com/revel/examples/chat

templateのjs

  var socket = new WebSocket('ws://'+window.location.host+'/websocket/room/socket?user={{.user}}')

  // Display a message
  var display = function(event) {
    $('#thread').append(tmpl('message_tmpl', {event: event}));
    $('#thread').scrollTo('max')
  }

  // Message received on the socket
  socket.onmessage = function(event) {
    display(JSON.parse(event.data))
  }

  $('#send').click(function(e) {
    var message = $('#message').val()
    $('#message').val('')
    socket.send(JSON.stringify(message))
  });

  $('#message').keypress(function(e) {
    if(e.charCode == 13 || e.keyCode == 13) {
      $('#send').click()
      e.preventDefault()
    }
  })

route

GET     /websocket/room                         WebSocket.Room
WS      /websocket/room/socket                  WebSocket.RoomSocket

top

<form action="{{url "Application.EnterDemo"}}">
      {{if .flash.error}}
        <p class="error">
          {{.flash.error}}
        </p>
      {{end}}
      <p>
        <label for="nick">Choose a nick name</label>
        <input type="text" name="user" id="user">
      </p>
      <p>
        <label for="nick">Demonstration</label>
        <select name="demo">
          <option></option>
          <option value="refresh">Ajax, active refresh</option>
          <option value="longpolling">Ajax, long polling</option>
          <option value="websocket">WebSocket</option>
        </select>
      </p>
      <p>
        <label></label>
        <input type="submit" id="enter" value="Enter the chat room">
      </p>
    </form>

controller

func (c Application) EnterDemo(user, demo string) revel.Result {
	c.Validation.Required(user)
	c.Validation.Required(demo)

	if c.Validation.HasErrors() {
		c.Flash.Error("Please choose a nick name and the demonstration type.")
		return c.Redirect(Application.Index)
	}

	switch demo {
	case "refresh":
		return c.Redirect("/refresh?user=%s", user)
	case "longpolling":
		return c.Redirect("/longpolling/room?user=%s", user)
	case "websocket":
		return c.Redirect("/websocket/room?user=%s", user)
	}
	return nil
}

controller

package controllers

import (
	"github.com/revel/revel"

	"github.com/revel/examples/chat/app/chatroom"
)

type WebSocket struct {
	*revel.Controller
}

func (c WebSocket) Room(user string) revel.Result {
	return c.Render(user)
}

func (c WebSocket) RoomSocket(user string, ws revel.ServerWebSocket) revel.Result {
	// Make sure the websocket is valid.
	if ws == nil {
		return nil
	}

	// Join the room.
	subscription := chatroom.Subscribe()
	defer subscription.Cancel()

	chatroom.Join(user)
	defer chatroom.Leave(user)

	// Send down the archive.
	for _, event := range subscription.Archive {
		if ws.MessageSendJSON(&event) != nil {
			// They disconnected
			return nil
		}
	}

	// In order to select between websocket messages and subscription events, we
	// need to stuff websocket events into a channel.
	newMessages := make(chan string)
	go func() {
		var msg string
		for {
			err := ws.MessageReceiveJSON(&msg)
			if err != nil {
				close(newMessages)
				return
			}
			newMessages <- msg
		}
	}()

	// Now listen for new events from either the websocket or the chatroom.
	for {
		select {
		case event := <-subscription.New:
			if ws.MessageSendJSON(&event) != nil {
				// They disconnected.
				return nil
			}
		case msg, ok := <-newMessages:
			// If the channel is closed, they disconnected.
			if !ok {
				return nil
			}

			// Otherwise, say something.
			chatroom.Say(user, msg)
		}
	}

	return nil
}

なるほど、なんとなく構成要素は分かったので、revelでやってみますか。

[Go言語] JSとGoでchatを作りたい その4

script.js

//
	document.getElementById("message").addEventListener("keydown", function(event){
		if (event.code === "Enter"){
			if(!socket){
				console.log("no connection")
				return false
			}
			event.preventDefault()
			event.stopPropagation()
			sendMessage()
		}
	})
//
function sendMessage(){
	console.log("Send Message...")
	let jsonData = {}
	jsonData["action"] = "broadcast"
	jsonData["username"] = document.getElementById("username").value
	jsonData["message"] = document.getElementById("message").value
	socket.send(JSON.stringify(jsonData))
	document.getElementById("message").value = ""
}

html

				<button id="submit" class="submit" onclick="sendMessage()">
					<i class="far far-paper-plane"></i>
				</button>

handler.go

func ListenToWsChannel(){
	var response domain.WsJsonResponse

	for {
		e := <-wsChan

		switch e.Action {
		case "username":
			clients[e.Conn] = e.Username
			users := getUserList()
			response.Action = "list_users"
			response.ConnectedUsers = users
			broadcastToAll(response)

		case "left":
			response.Action = "list_users"
			delete(clients, e.Conn)
			users := getUserList()
			response.ConnectedUsers = users
			broadcastToAll(response)
		case "broadcast":
			response.Action = "broadcast"
			response.Message = fmt.Sprintf(
				"<li class='replace'><strong>%s</strong>: %s</li>",
				e.Username,
				e.Message)
			broadcastToAll(response)
		}
	}
}

script.js

	socket.onmessage = msg => {
		let data = JSON.parse(msg.data)
		console.log({data})
		console.log("Action is", data.action)
		switch(data.action){
			case "list_users":
				let ul = document.getElementById("online-users")
				while (ul.firstChild) ul.removeChild(ul.firstChild)

				if (data.connected_users.length > 0){
					data.connected_users.forEach(function(item){
						let li = document.createElement("li")
						li.appendChild(document.createTextNode(item))
						ul.appendChild(li)
					})
				}
				break
			case "broadcast":
				let message = data.message
				let username = document.getElementById("username").value
				if (message.indexOf(username) > 0){
					message = message.replace("replace", "me")
				} else {
					message = message.replace("replace", "other")
				}
				messageList.innerHTML = messageList.innerHTML + message
				break
		}
	}

おおおおおおおおお、これは凄い
あとはrevelでやってfrontを書くか

[Go言語] JSとGoでchatを作りたい その3

usernameをserver側に送る

script.js

	let userInput = document.getElementById("username")
	userInput.addEventListener("change", function(){
		let jsonData = {}
		jsonData["action"] = "username"
		jsonData["username"] = this.value;

		socket.send(JSON.stringify(jsonData))
	})

handlers.go

func getUserList() []string {
	var clientList []string
	for _, client := range clients {
		if client != "" {
			clientList = append(clientList, client)
		}
	}
	sort.Strings(clientList)
	return clientList
}

connect.go

type WsJsonResponse struct {
	Action string `json:"action"`
	Message string `json:"message"`
	ConnectedUsers []string `json:"connected_users"`
}

script.js

	socket.onmessage = msg => {
		let data = JSON.parse(msg.data)
		console.log({data})
		console.log("Action is", data.action)
		switch(data.action){
			case "list_users":
				let ul = document.getElementById("online-users")
				while (ul.firstChild) ul.removeChild(ul.firstChild)

				if (data.connected_users.length > 0){
					data.connected_users.forEach(function(item){
						let li = document.createElement("li")
						li.appendChild(document.createTextNode(item))
						ul.appendChild(li)
					})
				}
				break
		}
	}

script.js

window.onbeforeunload = function(){
	console.log("User Leaving")
	let jsonData = {}
	jsonData["action"] = "left"
	socket.send(JSON.stringify(jsonData))
}
func ListenToWsChannel(){
	var response domain.WsJsonResponse

	for {
		e := <-wsChan

		switch e.Action {
		case "username":
			clients[e.Conn] = e.Username
			users := getUserList()
			response.Action = "list_users"
			response.ConnectedUsers = users
			broadcastToAll(response)

		case "left":
			response.Action = "list_users"
			delete(clients, e.Conn)
			users := getUserList()
			response.ConnectedUsers = users
			broadcastToAll(response)
		}
	}
}

ちょっとこんがらがってきました。

[Go言語] JSとGoでchatを作りたい その2

### Websocketのハンドリング
connection.go

package domain

import "github.com/gorilla/websocket"

type WsJsonResponse struct {
	Action string `json:"action"`
	Message string `json:"message"`
}

type WebSocketConnection struct {
	*websocket.Conn
}

type WsPlayload struct {
	Action String `json:"action"`
	Message string `json:"message"`
	Username string `json:"username"`
	Conn WebSocketConnection `json:"-"`
}

handlers.go

import (
	"chat/domain"
	"log"
	"net/http"
	"fmt"

	"github.com/CloudyKit/jet/v6"
	"github.com/gorilla/websocket"
)

var views = jet.NewSet(
	jet.NewOSFileSystemLoader("./html"),
	jet.InDevelopmentMode(),
)

var upgradeConnection = websocket.Upgrader {
	ReadBufferSize: 1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {return true},
}

var (
	wsChan = make(chan domain.WsPayload)

	clients = make(map[domain.WebSocketConnection]string)
)

func WsEndpoint(w http.ResponseWriter, r *http.Request){
	ws, err := upgradeConnection.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
	}
	log.Println("OK client connecting")

	conn := domain.WebSocketConnection{Conn: ws}
	clients[conn] = ""

	go ListenForWs(&conn)


	var response domain.WsJsonResponse
	response.Message = `<li>Connect to server</li>`

	err = ws.WriteJSON(response)
	if err != nil {
		log.Println(err)
	}
}

func Home(w http.ResponseWriter, r *http.Request){
	err := renderPage(w, "home.jet", nil)
	if err != nil {
		log.Println(err)
	}
}

func renderPage(w http.ResponseWriter, tmpl string, data jet.VarMap) error {
	view, err := views.GetTemplate(tmpl)
	if err != nil {
		log.Println(err)
		return err
	}

	err = view.Execute(w, data, nil)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func ListenForWs(conn *domain.WebSocketConnection){
	defer func(){
		if r := recover(); r != nil {
			log.Println("Error", fmt.Sprintf("%v", r))
		}
	}()

	var payload domain.WsPayload

	for {
		err := conn.ReadJSON(&payload)
		if err != nil {

		} else {
			payload.Conn = *conn
			wsChan <- payload
		}
	}
}

func broadcastToAll(response domain.WsJsonResponse){
	for client := range clients {
		err := client.WriteJSON(response)
		if err != nil {
			log.Println("websockets err")
			_ = client.Close()
			delete(clients, client)
		}
	}
}

func ListenToWsChannel(){
	var response domain.WsJsonResponse

	for {
		e := <-wsChan

		response.Action = "Got here"
		response.Message = fmt.Sprintf("Some message, and action was %s", e.Action)

		broadcastToAll(response)
	}
}

goroutineを使って、ListenForWs関数を別プロセスで呼び出すことで、ブラウザからの通信を常にキャッチし続ける状態を作る。gorouteは go と書くだけ

main.go

import (
	"chat/internal/handlers"
	"log"
	"net/http"
)

func main(){
	mux := routes()
	log.Println("Starting channel listener")
	go handlers.ListenToWsChannel()

	log.Println("Starting web server on port 8080")

	_ = http.ListenAndServe(":8080", mux)
}

successfully connected
script.js:21 {action: ”, message: ‘

  • Connect to server
  • ‘}

    2021/10/18 03:41:26 Error runtime error: invalid memory address or nil pointer dereference
    2021/10/18 03:41:30 Error repeated read on failed websocket connection
    2021/10/18 03:41:30 OK client connecting

    なんか上手くいってるっぽいが、どういう仕組みなのか全然理解できない…

    [Go言語] JSとGoでchatを作りたい その1

    $ mkdir go
    $ cd go
    $ go mod init chat
    $ mkdir -p cmd/web
    $ mkdir -p internal/handlers
    $ mkdir html
    $ touch cmd/web/main.go
    $ touch cmd/web/routes.go
    $ touch html/home.jet
    $ touch internal/handlers/handlers.go
    $ mkdir domain
    $ touch domain/connect.go
    $ mkdir static
    $ touch static/scripts.js
    $ touch static/style.css

    ### モジュールの追加
    $ go get github.com/CloudyKit/jet/v6
    $ go get github.com/bmizerany/pat
    $ go get github.com/gorilla/websocket

    ### handler, route.go, main.go省略
    internal/handlers/hanlders.go
    cmd/web/main.go
    cmd/web/routes.go

    ### template
    home/home.jet

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    	<link rel="stylesheet" href="/static/style.css">
    </head>
    <body>
    	<div class="chat-container">
    		<div class="chat-header">
    			<label for="username">Your Name</label>
    			<input type="text" id="username" class="username" autocomplete="off" placeholder=":) selfnote">
    		</div>
    		<div class="chat-body">
    			<ul id="message-list">
    				<li class="me">Sample</li>
    				<li class="other">Sample</li>
    			</ul>
    			<div class="send-area">
    				<input type="text" id="message" class="message" autocomplete="off" placeholder="message...">
    
    				<button id="submit" class="submit">
    					<i class="far far-paper-plane"></i>
    				</button>
    			</div>
    		</div>
    	</div>
    	<div class="oneline-user-container">
    		<ul id="online-users">
    			<li>XXXX</li>
    		</ul>
    	</div>
    	<script src="/static/scripts.js"></script>
    </body>
    </html>
    

    ### Websocketのエンドポイント作成
    /domain/connect.go
    L websocket返却用の構造体

    package domain
    
    type WsJsonResponse struct {
    	Action string `json:"action"`
    	Message string `json:"message"`
    }
    

    internal/handlers/hanlders.go

    import (
    	"chat/domain"
    	"log"
    	"net/http"
    
    	"github.com/CloudyKit/jet/v6"
    	"github.com/gorilla/websocket"
    )
    
    var upgradeConnection = websocket.Upgrader {
    	ReadBufferSize: 1024,
    	WriteBufferSize: 1024,
    	CheckOrigin: func(r *http.Request) bool {return true},
    }
    
    func WsEndpoint(w http.ResponseWriter, r *http.Request){
    	ws, err := upgradeConnection.Upgrade(w, r, nil)
    	if err != nil {
    		log.Println(err)
    	}
    	log.Println("OK client connecting")
    
    	var response domain.WsJsonResponse
    	response.Message = `<li>Connect to server</li>`
    
    	err = ws.WriteJSON(response)
    	if err != nil {
    		log.Println(err)
    	}
    }
    

    Route.go

    func routes() http.Handler {
    	mux := pat.New()
    
    	mux.Get("/", http.HandlerFunc(handlers.Home))
    	mux.Get("/ws", http.HandlerFunc(handlers.WsEndpoint)) // 追加
    
    	fileServer := http.FileServer(http.Dir("./static/"))
    	mux.Get("/static/", http.StripPrefix("/static", fileServer))
    
    	return mux
    }
    

    js

    let socket = null;
    
    document.addEventListener("DOMContentLoaded", function(){
    
    	socket = new WebSocket("ws://192.168.34.10:8080/ws")
    
    	socket.onopen = () => {
    		console.log("successfully connected")
    	}
    })
    

    console

    js

    document.addEventListener("DOMContentLoaded", function(){
    
    	socket = new WebSocket("ws://192.168.34.10:8080/ws")
    
    	socket.onopen = () => {
    		console.log("successfully connected")
    	}
    
    	socket.onclose = () => {
    		console.log("connection closed")
    	}
    
    	socket.onerror = error => {
    		console.log("there was an error")
    	}
    
    	socket.onmessage = msg => {
    		let j = JSON.parse(msg.data)
    		console.log(j)
    	}
    })
    

    うーむ、postされたらonloadというイメージなんだが…

    [Go言語] jet,patを使った書き方 : static追加

    $ tree
    ├── cmd
    │   └── web
    │   ├── main.go
    │   └── routes.go
    ├── go.mod
    ├── go.sum
    ├── html
    │   └── home.jet
    ├── internal
    │   └── handlers
    │   └── handlers.go
    └── static
    ├── script.js
    └── style.css

    routes.go

    package main
    
    import (
    	"webapp/internal/handlers"
    	"net/http"
    
    	"github.com/bmizerany/pat"
    )
    
    func routes() http.Handler {
    	mux := pat.New()
    
    	mux.Get("/", http.HandlerFunc(handlers.Home))
    
    	fileServer := http.FileServer(http.Dir("./static/"))
    	mux.Get("/static/", http.StripPrefix("/static", fileServer))
    	return mux
    }
    

    home.jet

    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    	<link rel="stylesheet" href="/static/style.css">
    </head>
    <body>
    	<h1>jet test</h1>
    	<script src="/static/scripts.js"></script>
    </body>
    </html>
    

    scripts.js

    alert("js is read")
    

    sytle.css

    h1 {
    	color: orange;
    }
    

    なるほど

    [Go言語] jetを使ったテンプレート・Handlers

    $ go mod init webapp
    $ mkdir -p cmd/web
    $ mkdir -p internal/handlers
    $ mkdir html
    $ touch cmd/web/main.go
    $ touch cmd/web/routes.go
    $ touch html/home.jet
    $ touch internal/handlers/handlers.go

    home.jetは、Webページのテンプレートファイル
    home.jet

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    </head>
    <body>
    	<h1>jet test</h1>
    </body>
    </html>
    

    handlers.go

    package handlers
    
    import (
    	"log"
    	"net/http"
    
    	"github.com/CloudyKit/jet/v6"
    )
    
    var views = jet.NewSet(
    	jet.NewOSFileSystemLoader("./html"),
    	jet.InDevelopmentMode(),
    )
    
    func Home(w http.ResponseWriter, r *http.Request){
    	err := renderPage(w, "home.jet", nil)
    	if err != nil {
    		log.Println(err)
    	}
    }
    
    func renderPage(w http.ResponseWriter, tmpl string, data jet.VarMap) error {
    	view, err := views.GetTemplate(tmpl)
    	if err != nil {
    		log.Println(err)
    		return err
    	}
    
    	err = view.Execute(w, data, nil)
    	if err != nil {
    		log.Println(err)
    		return err
    	}
    	return nil
    }
    

    route.go

    package main
    
    import (
    	"webapp/internal/handlers"
    	"net/http"
    
    	"github.com/bmizerany/pat"
    )
    
    func routes() http.Handler {
    	mux := pat.New()
    
    	mux.Get("/", http.HandlerFunc(handlers.Home))
    	return mux
    }
    

    main.go

    package main
    
    import (
    	"log"
    	"net/http"
    )
    
    func main(){
    	mux := routes()
    	log.Println("Starting web server on port 8080")
    
    	_ = http.ListenAndServe(":8080", mux)
    }
    

    $ go run cmd/web/*.go

    Echoとかを使わずに、jetなど色々な書き方があるんやな
    Webサーバーの作成には、bmizerany/patモジュールを使用

    [Go] Session管理

    manager.go

    package sessions
    
    import (
    	"crypto/rand"
    	"encoding/base64"
    	"errors"
    	"io"
    	"net/http"
    )
    
    type Manager struct {
    	database map[string]interface{}
    }
    
    var mg Manager
    
    func NewManager() *Manager {
    	return &mg
    }
    
    func (m *Manager) NewSessionID() string {
    	b := make([]byte, 64)
    	if _, err := io.ReadFull(rand.Reader, b); err != nil {
    		return ""
    	}
    	return base64.URLEncoding.EncodeToString(b)
    }
    
    fun (m *Manager) New(r *http.Request, cookieName string)(*Session, error){
    	cookie, err := r.Cookie(cookie.Name)
    	if err == nil && m.Exists(cookie.Value){
    		return nil, errors.New("sessionIDは既に発行されています")
    	}
    
    	session := NewSession(m, cookieName)
    	session.ID = m.NewSessionID()
    	session.request = r
    
    	return session, nil
    }
    
    func(m *Manager) Save(r *http.Request, w http.ResponseWriter, session *Session) error {
    	m.database[session.ID] = session
    
    	c := &http.Cookie {
    		Name: session.Name(),
    		Value: session.ID,
    		Path: "/",
    	}
    
    	http.SetCookie(session.writer, c)
    	return nil
    }
    
    
    func(m *Manager) Exists(sessionID string) bool {
    	_, r = m.database[sessionID]
    	return r
    }
    
    func(m *Manager) Get(r *http.Request, cookieName string)(*Session, error){
    	cookie, err := r.Cookie(cookieName)
    	if err != nil {
    		return nil, err
    	}
    
    	sessionID := cookie.Value
    	buffer, exists := m.database[sessionID]
    	if !exists {
    		return nil, errors.New("無効なセッションIDです")
    	}
    
    	session := buffer.(*Session)
    	session.request = r
    	return session, nil
    }
    
    func(m *Manager) Destroy(sessionID string){
    	delete(m.database, sessionID)
    }
    

    session.go

    package sessions
    
    import(
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    	"github.com/gorilla/context"
    )
    
    const (
    	DefaultSessionName = "default-session"
    	DefaultCookieName = "default-cookie"
    )
    
    type Session struct {
    	cookieName string
    	ID	string
    	manager *Manager
    	request *http.request
    	writer http.ResponseWriter
    	Values map[string]interface{}
    }
    
    func NewSession(manager *Manager, cookieName string) *Session {
    	return &Session {
    		cookieName: cookieName,
    		manager: manager,
    		Values: map[string]interface{}{},
    	}
    }
    
    func StartSession(sessionName, cookieName string, manager *Manager) gin.HandlerFunc {
    	return func(ctx *gin.Context){
    		var session *Session
    		var err error
    		session, err = manager.Get(ctx.Request, cookieName)
    		if err != nil {
    			session, err = manager.New(ctx.Request, cookieName)
    			if err != nil {
    				println(err.Error())
    				ctx.Abort()
    			}
    		}
    		session.writer = ctx.Writer
    		ctx.Set(sessionName, session)
    		defer context.Clear(ctx.Request)
    		ctx.Next()
    	}
    }
    
    func StartDefaultSession(manager *Manager) gin.HandlerFunc {
    	return StartSession(DefaultSessionName, DefaultCookieName, manager)
    }
    
    func GetSession(c *gin.Context, sessionName string) *Session {
    	return c.MustGet(sessionName).(*Session)
    }
    
    func GetDefaultSession(c *gin.Context) *Session {
    	return GetSession(c, DefaultSessionName)
    }
    
    func (s *Session) Save() error {
    	return s.manager.Save(s.request, s.writer, s)
    }
    
    func (s *Session) Name() string {
    	return s.cookieName
    }
    
    func (s *Session) Get(key string)(interface{}, bool){
    	ret, exists := s.Value[key]
    	return ret, exists
    }
    
    func(s *Session) Set(key string, val interface{}){
    	s.Values[key] = val
    }
    
    func(s *Session) Delete(key string){
    	delete(s.Values, key)
    }
    
    func(s *Session) Terminate(){
    	s.manager.Destroy(s.ID)
    }
    

    h_Login.go

    package req_handler
    
    import (
    	"fmt"
    	"html/template"
    	"net/http"
    )
    
    func HandlerLogin(w http.ResponseWrite, req *http.Request){
    
    	tpl := template.Must(template.ParseFiles("templates/login.gtpl"))
    
    	values := map[string]string{}
    
    	if err := tpl.ExecuteTemplate(w, "login.gtpl", values); err != nil {
    		fmt.Println(err)
    	}
    }
    
    fun HandlerExecLogin(w http.ResponseWriter, req *http.Request){
    
    	manager := sessions.NewManager()
    	sessions.StartDefaultSession(manager)
    
    	session := manager.Get(req, sessions.DefaultCookieName)
    	session.Set("account", req.FormValue("account"))
    	session.Save()
    
    	http.Redirect(w, req, "http://192.168.34.10:10443/login-top", 301)
    }
    

    h_LoginTop.go

    package req_handler
    
    import (
    	"fmt"
    	"html/template"
    	"net/http"
    )
    
    func HandlerLoginTop(w http.ResponseWriter, r *http.Request){
    	tpl := template.Must(template.ParseFiles("templates/login-top.gtpl"))
    
    	isLoggedIn := ""
    	account := ""
    
    	session := manager.Get(req, sessions.DefaultCookieName)
    	if session.Values["account"]{
    		isLoggedIn = "isLoggedIn"
    		account = session.Values["account"]
    	}
    
    	values := map[string] string {
    		"isLoggedIn": isLoggedIn,
    		"account": account,
    	}
    
    	if err := tpl.ExecuteTemplate(w, "login-top.gtpl", values); err != nil {
    		fmt.Println(err)
    	}
    }
    
    func HandlerExecLogout(w http.ResponseWriter, req *http.Request){
    
    	session := manager.Get(req, sessions.DefaultCookieName)
    	session.Terminate()
    
    	http.Redirect(w, req, "https://192.168.34.10:10443/login", 301)
    }
    

    なるほど、ginね
    1. usersテーブルでユーザ名とパスワードが一致するかチェックして、一致した場合進めるようにする
    2. sessionで管理する

    セッションの使い方は覚えていけば良さそう

    [Go] RevelでCRUD

    route

    GET     /                     App.Index
    GET     /team/:id             App.Show 
    GET     /edit/:id             App.Edit 
    POST	/edit/complete		  App.Update
    GET		/delete/:id			  App.Delete
    GET		/create				  App.Create
    POST	/store				  App.Store
    

    controller

    func (c App) Index() revel.Result {
    
    	result := []models.Baseballs{}
    	DB.Find(&result)
    
    	return c.Render(result)
    }
    
    func (c App) Show(id int) revel.Result {
    
    	result := []models.Baseballs{}
    	DB.Where("id = ?", id).First(&result)
    	team := result[0]
    	fmt.Println(team.Name)
    
    	return c.Render(team)
    }
    
    func (c App) Create() revel.Result {
    
    	return c.Render()
    }
    
    func (c App) Store() revel.Result {
    
    	name := c.Params.Get("name")
    	manager := c.Params.Get("manager")
    	home := c.Params.Get("home")
    
    	c.Validation.Required(name).Message("name is required")
    	c.Validation.Required(manager).Message("manager is required")
    	c.Validation.Required(home).Message("home is required")
    
    	if c.Validation.HasErrors(){
    		c.Validation.Keep()
    		c.FlashParams()
    		return c.Redirect(App.Create)
    	}
    
    	DB.Create(&models.Baseballs{
    		Name: name,
    		Manager: manager,
    		Home: home,
    	})
    
    	return c.Render(name)
    }
    
    func (c App) Edit(id int) revel.Result {
    
    	result := []models.Baseballs{}
    	DB.Where("id = ?", id).First(&result)
    	team := result[0]
    
    	return c.Render(team)
    }
    
    func (c App) Update() revel.Result {
    
    	id := c.Params.Get("id")
    	name := c.Params.Get("name")
    	manager := c.Params.Get("manager")
    	home := c.Params.Get("home")
    
    	DB.Model(models.Baseballs{}).Where("id = ?", id).Update(&models.Baseballs{
    		Name: name,
    		Manager: manager,
    		Home: home,
    	})
    
    	return c.Render(name)
    }
    
    func (c App) Delete(id int) revel.Result {
    
    	DB.Where("id = ?", id).Delete(&models.Baseballs{})
    
    	return c.Redirect(App.Index)
    }
    

    割と簡単にできますね
    migrationとかあるのかわからんが、取り敢えずログイン機能までは作りたい

    [Go] RevelでMySQLを操作

    conf/app.conf

    db.user = fuga
    db.password = hoge
    db.host = localhost
    db.port = 3306
    db.name = test
    db.protocol = tcp
    

    app/controllers/gorm.go

    package controllers
    
    import (
    	"github.com/revel/revel"
    	"github.com/jinzhu/gorm"
    	"strings"
    	"fmt"
    	_"github.com/go-sql-driver/mysql"
    )
    
    var DB *gorm.DB
    
    
    func InitDB(){
    	db, err := gorm.Open("mysql", getConnectionString())
    
    	if err != nil {
    		panic(err.Error())
    	}
    
    	db.DB()
    	DB = db
    }
    
    
    func getParamString(param string, defaultValue string) string {
    	p, found := revel.Config.String(param)
    	if !found {
    		if defaultValue == "" {
    			fmt.Sprintf("Could not find parameter: " + param)
    		} else {
    			return defaultValue
    		}
    	}
    	return p
    }
    
    
    func getConnectionString() string {
    	host := getParamString("db.host", "localhost")
    	port := getParamString("db.port", "3306")
    	user := getParamString("db.user", "root")
    	pass := getParamString("db.password", "password")
    	dbname := getParamString("db.name", "test")
    	protocol := getParamString("db.protocol", "tcp")
    	dbargs := getParamString("dbargs", "	")
    	timezone := getParamString("db.timezone", "parseTime=True")
    
    	if strings.Trim(dbargs, " ") != ""{
    		dbargs = "?" + dbargs
    	} else {
    		dbargs = ""
    	}
    
    	return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s?%s", user, pass, protocol, host, port, dbname, timezone)
    }
    

    app/models/idols.go

    package models
    
    type Idols struct {
    	ID int
    	Name string `json:"name"`
    	Label string `json:"label"`
    	Producer string `json:"producer"`
    	birth string `json:"birth"`
    	Member string `json:"member"`
    	Song string `json:"song"`
    }
    

    app/controllers/app.go

    package controllers
    
    import (
    	"github.com/revel/revel"
    	"myapp/app/models"
    	_"github.com/go-sql-driver/mysql"
    )
    
    type App struct {
    	*revel.Controller
    }
    
    func (c App) Index() revel.Result {
    
    	result := []models.Idols{}
    	DB.Where("id = ?", 1).First(&result)
    	greeting := result[0].Name
    	
    	return c.Render(greeting)
    }
    

    ### 更新

    	result := models.Idols{}
    	DB.First(&result, 5)
    	result.Song = "flash"
    	DB.Save(&result)
    

    大体わかった
    後はデモを作るか topicは野球あたりにしましょう