m_shige1979のときどきITブログ

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

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

https://github.com/mshige1979

MacにXamarin Studioをインストールしてみました

特に意味はない

C#とか使えないけどクロスプラットフォームはちょっと便利そうなので入れてみました

環境

Mac Book Air

ダウンロード

氏名やメールアドレスなどを設定すること

f:id:m_shige1979:20160501202726p:plain

ダウンロードできない場合はここのリンクで手動ダウンロードする

f:id:m_shige1979:20160501202727p:plain

インストール

インストーラーを起動してインストールを開始する

f:id:m_shige1979:20160501202728p:plain

f:id:m_shige1979:20160501202729p:plain

f:id:m_shige1979:20160501202730p:plain

f:id:m_shige1979:20160501202731p:plain

f:id:m_shige1979:20160501202732p:plain

f:id:m_shige1979:20160501202733p:plain

f:id:m_shige1979:20160501202734p:plain

起動してサンプルアプリを作成

「New Solution」をクリックする

f:id:m_shige1979:20160501202735p:plain

「Xamarin Forms App」を選択する

f:id:m_shige1979:20160501202736p:plain

アプリ名を設定して次へ

f:id:m_shige1979:20160501202737p:plain

アプリ名を確認して作成

f:id:m_shige1979:20160501202738p:plain

作成した初期はビルドはiOSになっている

f:id:m_shige1979:20160501202739p:plain

実行

f:id:m_shige1979:20160501202740p:plain
※画面がでかいので50%くらいに縮小

androidの場合はandroidのプロジェクトを選びデバイスを指定する

f:id:m_shige1979:20160501202741p:plain
※Genumotionも起動している場合は選ぶことができる

androidを起動確認

f:id:m_shige1979:20160501202742p:plain

おわり

所感

iOSandroidを同じソースで編集できるのは便利な感じ
xamarinの使い方もc#という言語もほとんど知らないので時間をとって少しずつ勉強していくことにしてみる

CentOS7.xをGUIを有効にしてインストール

ライセンスの設定が必要みたい

普段はvagrantとかでいれたり、最小パッケージとか入れるけどちょっとGUIを見てみたくなったので設定

環境

Mac Book Air
VMWare Fusion8

ダウンロード

CentOS-7-x86_64-DVD-1511.iso

インストール

インストール開始

f:id:m_shige1979:20160429110754p:plain

言語を設定

f:id:m_shige1979:20160429110803p:plain

設定画面を確認

f:id:m_shige1979:20160429110810p:plain

ソフトウェアの選択でGUIのアプリを設定する

f:id:m_shige1979:20160429110818p:plain

あとは適当にいれてからインストールする

f:id:m_shige1979:20160429110826p:plain

インストール中の画面

f:id:m_shige1979:20160429110833p:plain

ルートパスワード

f:id:m_shige1979:20160429110841p:plain

ユーザーの作成

f:id:m_shige1979:20160429110848p:plain

インストール完了後は再起動を行う

f:id:m_shige1979:20160429110856p:plain

ライセンス設定

ライセンス設定の確認が表示される

f:id:m_shige1979:20160429111406p:plain

質問に答えて同意するようにする

f:id:m_shige1979:20160429111438p:plain

初期設定

ログインする

f:id:m_shige1979:20160429111516p:plain

言語設定

f:id:m_shige1979:20160429111531p:plain

キーボード設定

f:id:m_shige1979:20160429111545p:plain

オンラインアカウントを設定(スキップ可能)

f:id:m_shige1979:20160429111602p:plain

設定完了

f:id:m_shige1979:20160429111610p:plain

マニュアル

f:id:m_shige1979:20160429111618p:plain


おわり

所感

最小構成の場合は10分〜15分くらいで終わるけどこの場合は30分くらいかかった…
visual studio codeとかlinuxでも動くらしいのでちょっと試してみたいかも…

cakephp3で複数データをインサート

データを登録する際

1件ずつ登録するか、一括で登録するか…

環境

CentOS7.x
php7.0.3
cakephp3.2.3

一括で登録

sample1

src/Shell/Sample1Shell.php

<?php
namespace App\Shell;

use Cake\Console\Shell;
use Cake\ORM\TableRegistry;

/**
 * Sample1 shell command.
 */
class Sample1Shell extends Shell
{

    /**
     * main() method.
     *
     * @return bool|int Success or error code.
     */
    public function main() 
    {

        // サンプルデータ準備
        $postList = [
            [
                "title" => "aaa1",
                "body" => "hogehoge1",
                "created" => date("Y-m-d H:i:s", strtotime("now")),
                "modified" => date("Y-m-d H:i:s", strtotime("now")),
            ],
            [
                "title" => "aaa2",
                "body" => "hogehoge2",
                "created" => date("Y-m-d H:i:s", strtotime("now")),
                "modified" => date("Y-m-d H:i:s", strtotime("now")),
            ],
            [
                "title" => "aaa3",
                "body" => "hogehoge3",
                "created" => date("Y-m-d H:i:s", strtotime("now")),
                "modified" => date("Y-m-d H:i:s", strtotime("now")),
            ],
        ];

        // テーブルオブジェクトを取得
        $posts = TableRegistry::get("Posts");
        // クエリーオブジェクトを取得
        $oQuery = $posts->query();

        // 配列単位にデータを設定
        foreach($postList as $post){
            // 1行ずつ設定
            $oQuery
                ->insert(['title', 'body', 'created', 'modified']) // キーを指定
                ->values($post);    // 明細を設定
        }

        // 実行
        $oQuery->execute();
    }
}

※モデルのsaveではないのでcreatedやmodifiedは任意で設定する必要があります。

SQL
INSERT INTO posts (
  title, body, created, modified
) VALUES 
 ('aaa1', 'hogehoge1', '2016-03-08 13:08:31', '2016-03-08 13:08:31'), 
  ('aaa2', 'hogehoge2', '2016-03-08 13:08:31', '2016-03-08 13:08:31'), 
  ('aaa3', 'hogehoge3', '2016-03-08 13:08:31', '2016-03-08 13:08:31')

一括で実行されています

一件ずつ登録

sample2

src/Shell/Sample2Shell.php

<?php
namespace App\Shell;

use Cake\Console\Shell;
use Cake\ORM\TableRegistry;

/**
 * Sample2 shell command.
 */
class Sample2Shell extends Shell
{

    /**
     * main() method.
     *
     * @return bool|int Success or error code.
     */
    public function main() 
    {
        // サンプルデータ準備
        $postList = [
            [
                "title" => "bbb1",
                "body" => "foofoo1",
            ],
            [
                "title" => "bbb2",
                "body" => "foofoo2",
            ],
            [
                "title" => "bbb3",
                "body" => "foofoo3",
            ],
        ];

        // テーブルオブジェクトを取得
        $posts = TableRegistry::get("Posts");

        // データを設定して複数のエンティティを取得
        $postsEntities = $posts->newEntities($postList);

        // データ数分保存する
        foreach($postsEntities as $entity){
            // 保存
            $posts->save($entity, ['atomic' => false]);
        }
    }
}

※createdなどは自動設定してくれる

SQL
INSERT INTO posts (title, body, created, modified) VALUES ('bbb1', 'foofoo1', '2016-03-08 13:23:24', '2016-03-08 13:23:24')
INSERT INTO posts (title, body, created, modified) VALUES ('bbb2', 'foofoo2', '2016-03-08 13:23:24', '2016-03-08 13:23:24')
INSERT INTO posts (title, body, created, modified) VALUES ('bbb3', 'foofoo3', '2016-03-08 13:23:24', '2016-03-08 13:23:24')

一件ずつ実行

所感

一括で登録できた方が楽なのでそちらを使う事になると思われる。変なデータになっていないかは別の方法でチェックして対応する
ビジネスロジックの場所に困りそうな状況になりつつあるけどまあ最初はコントローラーを困らせていこう(^ ^)

cakephp3でエラー画面

エラー画面を多少はカスタマイズしたい…

エラー内容自体はともかく、デフォルトのフォーマットはマズイし…

環境

CentOS7.x
php7.0.3
cakephp3.2.3

実装

エラー用コントローラーを作成

src/Controller/AppErrorController.php

<?php

namespace App\Controller;

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

/**
 * 独自のエラーコントローラー
 * Class AppErrorController
 * @package App\Controller
 */
class AppErrorController extends ErrorController
{
    /**
     * 描画前処理
     *
     * @param Event $event
     */
    public function beforeRender(Event $event)
    {
        // エラーの共通レイアウト名を指定
        // src/Template/Layout/error_layout.ctp
        $this->viewBuilder()->layout('error_layout');

        // エラーテンプレートパスを指定
        $this->viewBuilder()->templatePath('Error');
    }
}

※描画前にテンプレートパスやレイアウトを指定する

レンダラーを作成

src/Error/AppExceptionRenderer.php

<?php
namespace App\Error;

use Cake\Error\ExceptionRenderer;
use Exception;
use Cake\Log\Log;
use App\Controller\AppErrorController;

/**
 * 独自例外用レンダラー
 *
 * Class AppExceptionRenderer
 * @package App\Error
 */
class AppExceptionRenderer extends ExceptionRenderer
{

    /**
     * 独自のコントローラーを指定
     * @param Exception $exception
     * @return AppErrorController
     */
    protected function _getController()
    {
        // 独自のコントローラーを指定
        return new AppErrorController();
    }

    /**
     * 独自のテンプレート名を指定
     *
     * @param Exception $exception
     * @param string $method
     * @param int $code
     * @return string
     */
    protected function _template(Exception $exception, $method, $code)
    {
        // src/Template/Error/error_custom.ctp
        return $this->template = "error_custom";
    }
}

※テンプレートを指定

設定ファイルを編集

config/app.php

<?php
return [
    'Error' => [
        'errorLevel' => E_ALL & ~E_DEPRECATED,
        //'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
        'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
        'skipLog' => [],
        'log' => true,
        'trace' => true,
    ],

※レンダラーを変更

エラー用レイアウトを作成

src/Template/Layout/error_layout.ctp

<!DOCTYPE html>
<html>
<head>
    <?= $this->Html->charset() ?>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>
        <?= $this->fetch('title') ?>
    </title>
    <?= $this->Html->meta('icon') ?>
    <?= $this->fetch('meta') ?>

    <link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/app.css">
    <?= $this->fetch('css') ?>

</head>
<body>
    <header id="header">
        <nav class="navbar navbar-default">
            <div class="container">
                <a class="navbar-brand" id="logo" href="/">
                    CakePHP3 Demo
                </a>
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav navbar-right hidden-sm">
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-12">
                <div style="">
                    <?= $this->Flash->render() ?>
                </div>
            </div>
        </div>
    </div>
    <div class="container-fluid">
        <?= $this->fetch('content') ?>
    </div>
    <footer>
    </footer>
    <script src="/js/jquery-2.2.1.min.js"></script>
    <script src="/bootstrap/js/bootstrap.min.js"></script>
    <?= $this->fetch('script') ?>
</body>
</html>

エラー用テンプレートを作成
<?php
use Cake\Core\Configure;
use Cake\Error\Debugger;
?>
<div class="row">
    <div class="col-md-12">
        <div style="width: 800px;margin: 0 auto;">
            <h2><?= __d('cake', 'An Internal Error Has Occurred') ?></h2>
            <p class="error">
                <strong><?= __d('cake', 'Error') ?>: </strong>
                <?= h($message) ?>
            </p>
            <?php if (!empty($error->queryString)) : ?>
                <p class="notice">
                    <strong>SQL Query: </strong>
                    <?= h($error->queryString) ?>
                </p>
            <?php endif; ?>
            <?php if (!empty($error->params)) : ?>
                    <strong>SQL Query Params: </strong>
                    <?= Debugger::dump($error->params) ?>
            <?php endif; ?>
            <?php if ($error instanceof Error) : ?>
                    <strong>Error in: </strong>
                    <?= sprintf('%s, line %s', str_replace(ROOT, 'ROOT', $error->getFile()), $error->getLine()) ?>
            <?php endif; ?>
            <?php
                echo $this->element('auto_table_warning');

                if (extension_loaded('xdebug')):
                    xdebug_print_function_stack();
                endif;

                $this->end();
            ?>

        </div>
    </div>
</div>

画面

f:id:m_shige1979:20160308004400p:plain
スタックトレースはおいおい対応する

所感

エラー画面って難しいと思います。よくわからないのでログ吐きまくって調べてしまった。
まだおかしい部分があるのですか今回はこれでなんとかする。

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");
			},
		});
	});
});

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

結果

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

今回はこれで終わり