m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

cakephp3でfinderでログイン条件を追加

cakephp3になってからというか少しずつ変化しているものがあります

認証処理とかも…

環境

CensOS7.x
php 7.0.3
cakephp3.2.3

認証処理で条件を付ける際にfinderを使う

以前は"scope"がありましたがそれとは別に"finder"というものがあります。
Usersテーブルとは別にテーブルのチェックなどが必要な場合はこちらがあると便利な感じ

実装

AppController

src/Controller/AppController.php

<?php
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\Event;

class AppController extends Controller
{

    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');

        // 認証コンポーネント
        $this->loadComponent('Auth', [
            // ログイン後のリダイレクト
            'loginRedirect' => [
                'controller' => 'Posts',
                'action' => 'index'
            ],
            // ログアウト後のリダイレクト
            'logoutRedirect' => [
                'controller' => 'Users',
                'action' => 'login',
            ],
            // 認証
            'authenticate' => [
                // フォーム認証
                'Form' => [
                    // 暗号化
                    'passwordHasher' => [
                        'className' => 'Default',
                    ],
                    // 認証フィールド
                    'fields' => [
                        'username' => 'username',
                        'password' => 'password'
                    ],
                    // モデル
                    'userModel' => 'Users',
                    // 抽出メソッド
                    'finder' => 'login'
                ]
            ],
            // データ保存
            'storage' => 'Session',

            // 権限
            // isAuthorizedメソッドを実装する必要がある
            'authorize' => ['Controller'],
        ]);
    }

    /**
     * アクション前処理
     * @param Event $event
     */
    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);
    }

    /**
     * Before render callback.
     *
     * @param \Cake\Event\Event $event The beforeRender event.
     * @return void
     */
    public function beforeRender(Event $event)
    {
        if (!array_key_exists('_serialize', $this->viewVars) &&
            in_array($this->response->type(), ['application/json', 'application/xml'])
        ) {
            $this->set('_serialize', true);
        }
    }

    /**
     * 認証権限判定処理
     *
     * @param $user
     * @return bool
     */
    public function isAuthorized($user)
    {
        // ユーザーの権限が"admin"の場合は全てを許可する
        if (isset($user['role']) && $user['role'] === 'admin') {
            return true;
        }

        // Default deny
        return false;
    }
}

※finderという項目に"login"を指定します。省略時は"all"

UsersTable.php

src/Model/Table/UsersTable.php

<?php
namespace App\Model\Table;

use App\Model\Entity\User;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Users Model
 *
 */
class UsersTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('users');
        $this->displayField('id');
        $this->primaryKey('id');

        $this->addBehavior('Timestamp');
    }

    /**
     * ログイン用メソッド
     *
     * 独自のfindメソッド
     * @param Query $query
     * @param array $options
     * @return Query
     */
    public function findLogin(Query $query, array $options){
        // 条件を付与
        $query->where([
            'Users.status' => 0,
            'Users.deleted' => 0
        ]);
        return $query;
    }

}

SQLのログ

SELECT 
    Users.id AS `Users__id`, 
    Users.username AS `Users__username`, 
    Users.password AS `Users__password`, 
    Users.role AS `Users__role`, 
    Users.status AS `Users__status`, 
    Users.deleted AS `Users__deleted`, 
    Users.created AS `Users__created`, 
    Users.modified AS `Users__modified` 
FROM 
    users Users 
WHERE 
    (
        Users.username = 'test01' 
        AND Users.status = 0 
        AND Users.deleted = 0
    ) 
LIMIT 1

所感

HTMLのヘルパーって使いたくない…
生成されるまでどんな形かわからないんですよ…
多少手間がかかってもHTMLで書くべきかと思います…

なんでHTMLヘルパーってあるんだろう…

cakephp3でカスタムバリデーションプロバイダを作成

バリデーションのルールはチェックはいろいろありますが

多少細かいチェックなどがあった場合は対応できません。
各テーブルだけの機能ならともかくいろいろなテーブルクラスで使用する場合はバリデーションを使いまわしたい

環境

CentOS 7.x
php7.0.3
cakephp3.2.3

日本語のマニュアルがあると調査も捗る…少し放置していた隙に3.2行っていたのか…

対応

ディレクトリを作成
mkdir -p src/Model/Validation
バリデーションインターフェースを実装

src/Model/Validation/CustomValidationInterface.php

<?php
namespace App\Model\Validation;


interface CustomValidationInterface
{
    // チェックメソッドを定義する
    public static function sampleCheck1($string);

    // チェックメソッドを定義する
    public static function sampleCheck2($string, $len);
}
チェック処理を実装

src/Model/Validation/CustomValidation.php

<?php
namespace App\Model\Validation;

use Cake\Validation;

class CustomValidation implements CustomValidationInterface
{
    // チェックメソッドを実装する
    public static function sampleCheck1($check)
    {
        $result = null;
        if ($check == "check1") {
            $result = true;
        } else {
            $result = false;
        }
        return $result;
    }

    // チェックメソッドを実装する
    public static function sampleCheck2($check, $len)
    {
        $result = null;
        if ($check == "1234567890" && strlen($check) == $len) {
            $result = true;
        } else {
            $result = false;
        }
        return $result;
    }
}

※サンプルって手抜きの方がわかりやすいと思います

テーブルのバリデーションに実装
<?php
namespace App\Model\Table;

use App\Model\Entity\Post;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use App\Model\Validation;

/**
 * Posts Model
 *
 */
class PostsTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('posts');
        $this->displayField('title');
        $this->primaryKey('id');

        $this->addBehavior('Timestamp');
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {

        // プロバイダを追加
        $validator->provider('custom', Validation\CustomValidation::class);

        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->requirePresence('title', 'create')
            ->notEmpty('title')
            ->add("title", [
                "check1" => [
                    "rule" => ["sampleCheck2", 10],
                    "message" => "カスタムプロバイダーエラー1",
                    "provider" => "custom",  // 使用するプロバイダ名を設定
                ]
            ]);

        $validator
            ->requirePresence('body', 'create')
            ->notEmpty('body');

        return $validator;
    }

}

こんな感じ

所感

cakephp2の場合はビヘイビアに組み込むという方法で共通化していたので別の場所に配置できるようになるとメンテナンスもやりやすくなるかも…
データだけのチェックの場合とデータベースが絡む場合で難易度が変わるかもしれないので注意する必要があるかもしれないけど…

golangでajaxを使用してrssのデータを取得する

データベース処理考え中

ORMマッパーとかあれば簡単だけどSQLを書きたい欲求もあり、コードを冗長化させない点を…

RSSのデータをjsonで取得

単純に記事のタイトルとリンクなどを取得するものを作成

構成

.
├── main.go
├── static
│   ├── css
│   │   ├── bootstrap-theme.css
│   │   ├── bootstrap-theme.css.map
│   │   ├── bootstrap-theme.min.css
│   │   ├── bootstrap-theme.min.css.map
│   │   ├── bootstrap.css
│   │   ├── bootstrap.css.map
│   │   ├── bootstrap.min.css
│   │   └── bootstrap.min.css.map
│   ├── fonts
│   │   ├── glyphicons-halflings-regular.eot
│   │   ├── glyphicons-halflings-regular.svg
│   │   ├── glyphicons-halflings-regular.ttf
│   │   ├── glyphicons-halflings-regular.woff
│   │   └── glyphicons-halflings-regular.woff2
│   └── js
│       ├── app.js
│       ├── bootstrap.js
│       ├── bootstrap.min.js
│       ├── jquery-2.2.0.min.js
│       ├── jquery-2.2.0.min.map
│       └── npm.js
└── templates
    └── index.html

実装

main.go
package main

// インポート
import (
	"log"
	"net/http"
	"html/template"
	"github.com/gorilla/mux"
	"io/ioutil"
	"encoding/xml"
	"encoding/json"
	"fmt"
)

// RSS一覧用
type RssListItem struct {
	Title       string
	Url         string
}

// RSS2用の構造体
type Rss2 struct {
	XMLName		xml.Name	`xml:"rss"`
	Version		string		`xml:"version,attr"`
	// Required
	Title		string		`xml:"channel>title"`
	Link		string		`xml:"channel>link"`
	Description	string		`xml:"channel>description"`
	// Optional
	PubDate		string		`xml:"channel>pubDate"`
	ItemList	[]Item		`xml:"channel>item"`
}

// RSS2の各記事の部分(Item)
type Item struct {
	// Required
	Title		string		`xml:"title"`
	Link		string		`xml:"link"`
	Description	template.HTML	`xml:"description"`
	// Optional
	Content		template.HTML	`xml:"encoded"`
	PubDate		string		`xml:"pubDate"`
	Comments	string		`xml:"comments"`
}

// RSSのjson用
type RssJsonList struct {
	Title    string `json:"title"`
	List     []RssJsonItem `json:"list"`
}
type RssJsonItem struct {
    Title     string `json:"title"`
    Link      string `json:"link"`
	Date      string `json:"date"`
}

// 主処理
func main(){

	// ルーティング初期化
	r := mux.NewRouter()

	// 静的ファイルをサポート
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

	// url別処理
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){

		// テンプレート生成
        tmpl := template.Must(template.New("index").ParseFiles("templates/index.html"))
        // 変数を設定
        tmpl.Execute(w, struct{
			RssList []RssListItem
		}{
			RssList: []RssListItem{
				{
					Title: "m_shige1979のささやかな抵抗と欲望の日々",
					Url: "http://m-shige1979.hatenablog.com/rss",
				},
				{
                    Title: "ぎひょーさん",
                    Url: "http://gihyo.jp/feed/rss2",
                },
				{
                    Title: "@IT",
                    Url: "http://rss.rssad.jp/rss/itmatmarkit/rss.xml",
                },
			},
		})

		// log
		log.Print(r.URL.Path)

	})

	r.HandleFunc("/getRss", func(w http.ResponseWriter, r *http.Request){

		r.ParseForm()
		_url := r.FormValue("url")

		// httpよりRSS取得
		res, err := http.Get(_url)
		if err != nil {
			panic(err)
		}

		// text形式で読み込む
		asText, err2 := ioutil.ReadAll(res.Body)
		if err2 != nil {
			panic(err2)
		}

		// rss2構造体を定義
		rssData := Rss2{}

		// パースして格納
		err3 := xml.Unmarshal(asText, &rssData)
		if err3 != nil {
			panic(err3)
		}

		// json用に新規に用意
		var jsonData = RssJsonList{}
		jsonData.Title = rssData.Title
		jsonData.List = []RssJsonItem{}

		// 記事の部分を取得
		for _, value := range rssData.ItemList {
			jsonData.List = append(jsonData.List, RssJsonItem{Title: value.Title, Link: value.Link, Date: value.PubDate})
		}

        // jsonエンコード
        outputJson, err := json.Marshal(&jsonData)
        if err != nil {
            panic(err)
        }

        // jsonヘッダーを出力
        w.Header().Set("Content-Type", "application/json")

        // jsonデータを出力
        fmt.Fprint(w, string(outputJson))

		// log
		log.Print(r.URL.Path)
	})

	// ルーティング設定
	http.Handle("/", r)

	// ポート待ち
	log.Println(":9001")
	http.ListenAndServe(":9001", nil)
}
templates/index.html
{{ define "index" }}
<!DOCType html>
<html>
<head>
	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>websample3</title>
	<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
</head>
<body>
	<div class="container-fluid">
		<div class="row">
			<div class="col-xs-12">
				<h3>RSSリスト</h3>
				<ul>
					{{ range .RssList }}
					<li>
						<a href="javascript:void(0);" class="rss_click" data-url="{{ .Url }}">
							{{ .Title }}
						</a>
					</li>
					{{ end }}
				</ul>
			</div>
		</div>
		<hr />
		<div class="row">
			<div class="col-xs-12">
				<div class="rss_entry">
				</div>
				<div class="rss_item_tag" style="display: none;">
					<blockquote>
						<p class="date">aaaa</p>
						<p class="title">bbbb</p>
						<footer>
							<a href="cccc" target="_blank">xxxxx</a>
						</footer>
					</blockquote>
				</div>
			</div>
		</div>
	</div>
	<script src="/static/js/jquery-2.2.0.min.js"></script>
	<script src="/static/js/bootstrap.min.js"></script>
	<script src="/static/js/app.js"></script>
</body>
</html>
{{ end }}
static/js/app.js
$(function(){
	$(".rss_click").on("click", function(){
		var _url = $(this).attr("data-url");
		console.log(_url);

		// ajaxでrssを取得
		$.ajax({
			type: 'POST',
			url: '/getRss',
			data: {
				url: _url
			},
			dataType: 'json',
			success: function(data, status){
				console.log("success");
				console.log(data);

				// クリア
				$(".rss_entry").empty();
				for(var _key in data.list){
					var _itemElement = $(".rss_item_tag").clone(true);
					_itemElement.removeClass("rss_item_tag");
					_itemElement.css("display", "block");

					// rss情報設定
					_itemElement.find(".date").text(data.list[_key]["date"]);
					_itemElement.find(".title").text(data.list[_key]["title"]);
					_itemElement.find("footer > a").attr("href", data.list[_key]["link"]);
					_itemElement.find("footer > a").text(data.list[_key]["link"]);

					// 追加
					$(".rss_entry").append(_itemElement);
				}
			},
			error: function(err1, err2, err3){
				console.log("error");
			},
		});
	});
});

ソースのインデントが…まあいいか

結果

うん、まあこんなもんでいいかと

今回はこれで終わり

golangでjsonのデータを出力する

一気に気温が上がったり、下がったり大変

安定せんかな〜

jsonでデータを出力する

まあ、今回は単純にjsonを出す

実装1(シンプルな構造体)

main1.go
package main

import (
	"fmt"
	"net/http"
	"log"
	"github.com/gorilla/mux"
	"encoding/json"
)

// 単純な構造体
type Data1 struct {
	Title    string `json:"title"`
	Message  string `json:"message"`
	Status   int    `json:"status"`
}

func main(){

	// ルーティング
	router := mux.NewRouter()

	// URL別設定
	router.HandleFunc("/sample1", func(w http.ResponseWriter, r  *http.Request){

		// 構造体を定義
		var data1 = Data1{}
		data1.Title = "sample1"
		data1.Message = "hello, sample1"
		data1.Status = 100

		// jsonエンコード
		outputJson, err := json.Marshal(&data1)
		if err != nil {
			panic(err)
		}

		// jsonヘッダーを出力
		w.Header().Set("Content-Type", "application/json")

		// jsonデータを出力
		fmt.Fprint(w, string(outputJson))

		// log
		log.Print(r.URL.Path)
	})

	// ハンドル割当
	http.Handle("/", router)

	// log
	log.Print("localhost:9001")

	// ポート
	http.ListenAndServe(":9001", nil)

}

f:id:m_shige1979:20160216215650p:plain

実装2(構造体に配列を含む)

main2.go
package main

import (
	"fmt"
	"net/http"
	"log"
	"github.com/gorilla/mux"
	"encoding/json"
)

// 配列を含む構造体
type Data2 struct {
	Title    string `json:"title"`
	Message  string `json:"message"`
	Status   int    `json:"status"`
	List	 map[string]Item `json:"list"`
}

// 構造体の配列Item
type Item struct {
	Name     string `json:"name"`
	Age      int    `json:"age"`
}

func main(){

	// ルーティング
	router := mux.NewRouter()

	// URL別設定
	router.HandleFunc("/sample2", func(w http.ResponseWriter, r  *http.Request){

		// 構造体を定義
		var data2 = Data2{}
		data2.Title = "sample2"
		data2.Message = "hello, sample2"
		data2.Status = 200
		data2.List = map[string]Item{}
		data2.List["test1"] = Item{Name:"taro", Age: 10}
		data2.List["test2"] = Item{Name:"hanako", Age: 18}
		data2.List["test3"] = Item{Name:"pochi", Age: 50}

		// jsonエンコード
		outputJson, err := json.Marshal(&data2)
		if err != nil {
			panic(err)
		}

		// jsonヘッダーを出力
		w.Header().Set("Content-Type", "application/json")

		// jsonデータを出力
		fmt.Fprint(w, string(outputJson))

		// log
		log.Print(r.URL.Path)
	})

	// ハンドル割当
	http.Handle("/", router)

	// log
	log.Print("localhost:9001")

	// ポート
	http.ListenAndServe(":9001", nil)

}

f:id:m_shige1979:20160216215705p:plain

なんとかできました。
RSSの場合にもあったけど

type Data2 struct {
	Title    string `json:"title"`
	Message  string `json:"message"`
	Status   int    `json:"status"`
	List	 map[string]Item `json:"list"`
}

右の方にjsonとか指定したらその名前になるので小文字にしたい場合などは役に立ちます…というかきまりみたいな感じかな…
テキトーにググったけどそれっぽいパッケージが見つからなかったのでこれでいいはず

golangでRSS2のデータを取得して表示

XMLを取得してパースする

RSSを取得して一覧を表示するサンプルを作成する

参考

[Golang] XML Parsing Example (7) - Parse RSS 2.0
Atomや両方対応のパターンもあります

静的ファイルも参照したい

jsとかcssとか使用したいけどどうやって参照するか
web applications - Serving static content with a root URL with the Gorilla toolkit - Stack Overflow

実装

構成
.
├── main.go
├── static
│   ├── css
│   │   ├── bootstrap-theme.css
│   │   ├── bootstrap-theme.css.map
│   │   ├── bootstrap-theme.min.css
│   │   ├── bootstrap-theme.min.css.map
│   │   ├── bootstrap.css
│   │   ├── bootstrap.css.map
│   │   ├── bootstrap.min.css
│   │   └── bootstrap.min.css.map
│   ├── fonts
│   │   ├── glyphicons-halflings-regular.eot
│   │   ├── glyphicons-halflings-regular.svg
│   │   ├── glyphicons-halflings-regular.ttf
│   │   ├── glyphicons-halflings-regular.woff
│   │   └── glyphicons-halflings-regular.woff2
│   └── js
│       ├── bootstrap.js
│       ├── bootstrap.min.js
│       ├── jquery-2.2.0.min.js
│       ├── jquery-2.2.0.min.map
│       └── npm.js
└── templates
    └── index.html
main.go
package main

// インポート
import (
	"log"
	"net/http"
	"html/template"
	"github.com/gorilla/mux"
	"io/ioutil"
	"encoding/xml"
)

// RSS2用の構造体
type Rss2 struct {
	XMLName		xml.Name	`xml:"rss"`
	Version		string		`xml:"version,attr"`
	// Required
	Title		string		`xml:"channel>title"`
	Link		string		`xml:"channel>link"`
	Description	string		`xml:"channel>description"`
	// Optional
	PubDate		string		`xml:"channel>pubDate"`
	ItemList	[]Item		`xml:"channel>item"`
}

// RSS2の各記事の部分(Item)
type Item struct {
	// Required
	Title		string		`xml:"title"`
	Link		string		`xml:"link"`
	Description	template.HTML	`xml:"description"`
	// Optional
	Content		template.HTML	`xml:"encoded"`
	PubDate		string		`xml:"pubDate"`
	Comments	string		`xml:"comments"`
}

// 主処理
func main(){

	// ルーティング初期化
	r := mux.NewRouter()

	// 静的ファイルをサポート
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

	// url別処理
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
		// httpよりRSS取得
		_rssURL := "http://m-shige1979.hatenablog.com/rss"
		res, err := http.Get(_rssURL)
		if err != nil {
			panic(err)
		}

		// text形式で読み込む
		asText, err2 := ioutil.ReadAll(res.Body)
		if err2 != nil {
			panic(err2)
		}

		// rss2構造体を定義
		rssData := Rss2{}

		// パースして格納
		err3 := xml.Unmarshal(asText, &rssData)
		if err3 != nil {
			panic(err3)
		}

		// テンプレート生成
		tmpl := template.Must(template.New("index").ParseFiles("templates/index.html"))
		// 変数を設定
		tmpl.Execute(w, struct{
			RssItemList []Item
		}{
			RssItemList: rssData.ItemList,
		})

		// url
		log.Println(r.URL.Path)
	})

	// ルーティング設定
	http.Handle("/", r)

	// ポート待ち
	log.Println(":9001")
	http.ListenAndServe(":9001", nil)
}
templates/index.html
{{ define "index" }}
<!DOCType html>
<html>
<head>
	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>websample3</title>
	<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
</head>
<body>
	<div class="container-fluid">
		<div class="row">
			<div class="col-xs-12">
				<h3>RSSリスト</h3>
			</div>
		</div>
		<div class="row">
			<div class="col-xs-12">
				{{ range .RssItemList }}
				<div>
					<blockquote>
						<p>{{ .PubDate }}</p>
						<p>{{ .Title }}</p>
						<footer>
							<a href="{{ .Link }}" target="_blank">{{ .Link }}</a>
						</footer>
					</blockquote>
				</div>
				{{ end }}
			</div>
		</div>
	</div>
	<script src="/static/js/jquery-2.2.0.min.js"></script>
	<script src="/static/js/bootstrap.min.js"></script>
</body>
</html>
{{ end }}

結果

f:id:m_shige1979:20160211183714p:plain

所感

xmlの解析がなんかいい感じにやってくれたので助かる。
atomなどと一緒にやる場合は多少手間がかかるみたいですけど
構造体の仕組みをちょっと調べないと…

golangでgorillaとかいうのを使ってみる(sessions)

セッションを使用してみる

値を入れること自体はそんなに難しく無い感じですけど取り出すときになんかいろいろ手間取る感じ
基本的には構造体などで管理したほうがいい感じです

インストール

go get github.com/gorilla/sessions

実装

main.go
package main

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"net/http"
	"fmt"
	"time"
	"html/template"
	"crypto/rand"
	"encoding/base32"
	"io"
	"strings"
)

// セッション名
var session_name string = "gsid"

// Cookie型のstore情報
var store *sessions.CookieStore

// セッションオブジェクト
var session *sessions.Session

// 構造体
type Data1 struct {
	Count    int
	Msg      string
}

// 主処理
func main(){

	// 構造体を登録
	gob.Register(&Data1{})

	// セッション初期処理
	sessionInit()

	// ルーティング生成
	r := mux.NewRouter()

	// URL別の処理
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){

		// セッションオブジェクトを取得
		session, _ := store.Get(r, session_name)

		data1, ok := session.Values["data1"].(*Data1)
		if data1 != nil {
			data1.Count++
			data1.Msg = fmt.Sprintf("%d件カウント", data1.Count)
		} else {
			data1 = &Data1{0, "データ無し"}
		}
		fmt.Println(ok)
		fmt.Println(data1)
		session.Values["data1"] = data1

		// 保存
		sessions.Save(r, w)

		// テンプレートを指定
		tmpl := template.Must(template.New("index").ParseFiles("templates/index.html"))
		tmpl.Execute(w, struct {
			Detail *Data1
		}{
			Detail: data1,
		})

		// logの代わり
		fmt.Print(time.Now())
		fmt.Println(" url = " + r.URL.Path)
	})

	r.HandleFunc("/clear", func(w http.ResponseWriter, r *http.Request){

		// セッション初期化
		sessionInit()

		// logの代わり
		fmt.Print(time.Now())
		fmt.Println(" url = " + r.URL.Path)

		// redirect
		http.Redirect(w, r, "/", http.StatusFound)

	})

	// rを割当
	http.Handle("/", r)

	// ポートを割当
	fmt.Println("localhost:9001")
	http.ListenAndServe(":9001", nil)
}

// セッション用の初期処理
func sessionInit(){

	// 乱数生成
	b := make([]byte, 48)
	_, err := io.ReadFull(rand.Reader, b)
	if err != nil {
		panic(err)
	}
	str := strings.TrimRight(base32.StdEncoding.EncodeToString(b), "=")

	// 新しいstoreとセッションを準備
	store = sessions.NewCookieStore([]byte(str))
	session = sessions.NewSession(store, session_name)

	// セッションの有効範囲を指定
	store.Options = &sessions.Options{
		Domain:     "localhost",
		Path:       "/",
		MaxAge:     0,
		Secure:     false,
		HttpOnly:   true,
	}

	// log
	fmt.Println("key     data --")
	fmt.Println(str)
	fmt.Println("")
	fmt.Println("store   data --")
	fmt.Println(store)
	fmt.Println("")
	fmt.Println("session data --")
	fmt.Println(session)
	fmt.Println("")

}
templates/index.html
{{ define "index" }}
<!DOCType html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>websample2</title>
	</head>
	<body>
		count = {{ .Detail.Count }}<br />
		msg = {{ .Detail.Msg }}<br />
		<br />
		<a href="/clear">クリア<a/>
	</body>
</html>
{{ end }}


セッションのデータ管理は?

store = sessions.NewCookieStore([]byte(str))

これで"store"とかいう変数にCookie形式の形っぽいデータをどっかに管理するみたい
ちなみにローカルファイル内に配置したい場合は

store = sessions.NewFilesystemStore("", []byte(str))

こうなる
第1引数はセッションファイルの配置場所で空文字の場合は規定の場所を指定centosとかでは"/tmp"みたい…

byteで渡す値でデータを管理しているのでデータを初期化するという意味では一度別の名前にするのが良いかと
Valuesの値を消したりしてもいいかもしれないけど面倒な感じもあるので乱数を生成してから消すのも手かと思われる

所感

なんか遅い…
手動でログを出力しているせいかもしれないけどなんか応答が結構遅い感じがする
ルーティングとセッションをとりあえずだけど対応したのでちょっと他のこともやってみる

うーんやることが増えてきたのでソースがでかくなってきた\(^o^)/

golangでgorillaとかいうのを使ってみる(mux)

ゴリラ?

Gorilla, the golang web toolkit
まあ、http関連のやつでルーティングとかセッションとかを"net/http"よりは柔軟に使える感じのもの
なんか適当に調べたけど"net/http"ではセッションは使えないとかなんとか

ちょっと今回はルーティングの"gorilla/mux"を使用する

マニュアル

http://www.gorillatoolkit.org/pkg/mux

今回はさわりだけ…

インストール

go get github.com/gorilla/mux

実装

今回の構成
.
├── main.go
└── templates
    ├── index.html
    └── save.html
main.go
package main

import (
	"net/http"
	"github.com/gorilla/mux"
	"fmt"
	"time"
	"html/template"
)

func main(){

	// 生成
	r := mux.NewRouter()

	// "/"の場合の処理
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		// テンプレート
		tmpl := template.Must(template.New("index").ParseFiles("templates/index.html"))
		err := tmpl.Execute(w, nil)
		if err != nil {
			panic(err)
		}

		// logの代わり
		fmt.Print(time.Now())
		fmt.Printf(" path: " + r.URL.Path + "\n")
	}).Methods("GET")

	// "/save"の場合の処理
	r.HandleFunc("/save", func(w http.ResponseWriter, r *http.Request) {

		// formの値を取得
		r.ParseForm()
		data1 := r.FormValue("aaa")
		data2 := r.FormValue("bbb")

        // テンプレート
        tmpl := template.Must(template.New("save").ParseFiles("templates/save.html"))
        err := tmpl.Execute(w, struct {
			Data1 string
			Data2 string
		}{
			Data1: data1,
			Data2: data2,
		})
		if err != nil {
			panic(err)
		}

		// logの代わり
		fmt.Print(time.Now())
		fmt.Printf(" path: " + r.URL.Path + "\n")
	}).Methods("POST")

	// 可変URL1
	r.HandleFunc("/test1/{sample1}/", func(w http.ResponseWriter, r *http.Request) {
		// 画面サンプル
		fmt.Fprintf(w, "sample1\n")

		// logの代わり
		fmt.Print(time.Now())
		fmt.Printf(" path: " + r.URL.Path + "\n")
	})

	// 可変URL2
	r.HandleFunc("/test2/{sample1}/{id:[0-9]{1,6}}/", func(w http.ResponseWriter, r *http.Request) {
		// 画面サンプル
		fmt.Fprintf(w, "sample2\n")

		// logの代わり
		fmt.Print(time.Now())
		fmt.Printf(" path: " + r.URL.Path + "\n")
	})

	// handing
	http.Handle("/", r)

	// 設定
	http.ListenAndServe(":9001", nil)
}

※POST、GETだけを指定したい場合など、また一部のURLが可変になる場合は楽できます。

templates/index.html
{{ define "index" }}
<!DOCType html>
<html>
<head>
	<meta charaset="utf-8" />
	<title>websample1</title>
</head>
<body>
	<h3>なんかいれて送信してみて</h3>
	<form method="post" action="/save">
		<input type="text" name="aaa" value="" /><br />
		<input type="text" name="bbb" value="" /><br />
		<input type="submit" value="送信" />
	</form>
</body>
</html>
{{ end }}
templates/save.html
{{ define "save" }}
<!DOCType html>
<html>
<head>
	<meta charaset="utf-8" />
	<title>websample1</title>
</head>
<body>
	<h3>送信されたものは以下</h3>
	<div>
		aaa = {{ .Data1 }}<br />
		bbb = {{ .Data2 }}<br />
	</div>
	<a href="/">top</a>
</body>
</html>
{{ end }}

画面

f:id:m_shige1979:20160209223846p:plain

f:id:m_shige1979:20160209223856p:plain

f:id:m_shige1979:20160209223907p:plain

所感

このツールキットを使用することでネットワーク関連のコードを書くのに結構便利になる感じかと
web アプリを作成するとしたらルーティングとセッションは必須なのできちんとやっておく必要がありそうです。