2016年7月1日 星期五

Node.js筆記:Blog system(2)

Express generation 專案架構

當用express generation指令後會產生一個node project的資料夾.
enter image description here

  • app.js: 開機檔案, 或是說入口網站
  • bin: 存放執行檔
  • node_modules: 存放package.json中安裝的模組, 當你在package.json中新增相依的模組並安裝後, 該模組會存放在這個資料夾下.
  • package.json: 儲存著專案的資訊以及模組相依(dependencies), 當在dependencies中新增相依的模組時, 執行npm install, npm會檢查目前的資料下的package.json, 並自動安裝所有指定的模組
  • public: 存放image, css, js等文件
  • routes: 存放路由文件
  • views: 存放視圖檔, 或是說範本檔案

配置Routing

規劃網站各個頁面的路由路徑. 首先再次修改app.js: 新增apis路由, 用來負責POST方法的處理.

    ...
    var routes = require('./routes/index');
    var users = require('./routes/users');
    var apis = require('./routes/apis');

    ...
    app.use('/', routes);
    app.use('/users', users);
    app.use('/apis', apis);

設定user路由

在我們的設定中, user路由負責處理所有來自/user網址的要求, 例如新增文章, 會員註冊等. user路由會視用戶的請求而回應不同的資料.

新增首頁

首先在views資料夾中建立一個users資料夾. 裡面會放置所有user路由會用到的頁面.

首頁

1.更新index.jade檔
中間的if判斷是會根據使用者是否登入而顯示不一定的連結給使用者.

    doctype html
html
    head
        title= 'Blog System'
    body
        div(style='float:left;margin:10px;')
            a(href='/') Home

        div(style='float:right')
            if (username && authenticated)
                a(href='/users/signout', style='margin:10px;') Sign Out
                a(href='/users/add_article', style='margin:10px;') Add Article
                a(href='/users/profile', style='margin:10px;') Profile
                span #{username}
            else
                a(href='/users/signin', style='margin:10px;') Log In
                a(href='/users/register', style='margin:10px;') Register
                a(href='/users/forget', style='margin:10px;') Forgot password

    div(style='padding:50px;')
        h1= title

2.修改routes/index.js
把session所存的變數所存的變數傳回頁面.

    var express = require('express');
    var router = express.Router();

    /* GET home page. */
    router.get('/', function(req, res, next) {
        res.locals.username = req.session.name ;
        res.locals.authenticated = req.session.logined;
        res.render( 'index', {title : 'Blog System'});
    });
    module.exports = router;

使用者註冊頁面

1.新增views/users/register.jade
這邊的重點是寫了一個form的POST表單來處理使用者設定帳密

doctype html
html
    head
        title= Register
    body
        div(style='float:left;margin:10px;')
            a(href='/') Home

        div(style='float:right')
                a(href='/users/signin', style='margin:10px;') Log In
                a(href='/users/forget', style='margin:10px;') Forgot password

        div(style='padding:50px;')
            form(name="login", action="/apis/login", method="post")
                h1 Register
                div
                    p
                        label(style='padding-right:50px') Username or e-mail
                        input(type="text", name="user", placeholder='username@example.com')
                    p
                        label(style='padding-right:110px')  password
                        input(type="password", name="passwd")
                p
                    span(style='padding-right:260px')
                    input(type="submit", name="submit", value="Sing Up")

2.修改routes/user.js
新增一個判斷式, 如果logined是true, 代表使用者已經是登入狀態, 就會直接回到首頁.
如果尚未登入則會顯示register.jade的頁面.

var express = require('express');
var router = express.Router();

router.get('/register', function(req, res, next) {
    if (req.session.logined) {
        res.redirect('/');
        return;
    }
    res.render('users/register');
});

    ....

3.修改routes/apis.js
新增login函數, 處理註冊頁面傳來的POST方法.
先利用if來判斷使用者是否有輸入帳密, 當其中少一個的話則返回註冊畫面.
當輸入正確就把帳密給儲存到session中. 並把login設定為true代表使用者登入了.

ar express = require('express');
var router = express.Router();

router.post('/login', function(req, res, next) {
    if ((!req.body.user) || (!req.body.passwd)) {
        res.redirect('register');
        return;
    }
    req.session.name = req.body.user;
    req.session.passwd = req.body.passwd;
    req.session.logined = true;
    res.redirect('/');
});

...

使用者登入畫面

1.使用者登入頁面跟註冊的register.jade比, 只有差在字改成”Login”.

doctype html
html
    head
        title= Login
    body
        div(style='float:left;margin:10px;')
            a(href='/') Home

        div(style='float:right')
                a(href='/users/register', style='margin:10px;') Register
                a(href='/users/forget', style='margin:10px;') Forgot password

        div(style='padding:50px;')
            form(name="login", action="/apis/login", method="post")
                h1 Login
                div
                    p
                        label(style='padding-right:50px') Username or e-mail
                        input(type="text", name="user", placeholder='username@example.com')
                    p
                        label(style='padding-right:110px')  password
                        input(type="password", name="passwd")
                p
                    span
                        a(href='/users/forget', style='padding-right:175px') Forgot password
                    input(type="submit", name="submit", value="login")

2.修改routes/user.js

...

router.get('/signin', function(req, res, next) {
    if (req.session.logined) {
        res.redirect('/');
        return;
    }
    res.render('users/signin');
});

使用者登出頁面

1.修改routes/user.js
當登出之後會回到首頁.

...
router.get('/signout', function(req, res, next) {
    req.session.logined = false;
    res.redirect('/');
    res.end();
});

忘記密碼頁面

1.views/users/forget/jade

doctype html
html
    head
        title= 'Forget Password'
    body
        div(style='float:left;margin:10px;')
            a(href='/') Home

        div(style='float:right')
                a(href='/users/register', style='margin:10px;') Register
                a(href='/users/signin', style='margin:10px;') Log In

        div(style='padding:50px;')
            form(name="login", action="/apis/login", method="post")
                h1 Forget Password
                div
                    p
                        label(style='padding-right:50px') Username or e-mail
                        input(type="text", name="user", placeholder='username@example.com')
                p
                    span(style='padding-right:210px')
                    input(type="submit", name="submit", value="Forget Password")

2.修改routes/user.js

...

router.get('/forget', function(req, res, next) {
    if (req.session.logined) {
        res.redirect('/');
        return;
    }
    res.render('users/forget');
});

2016年6月30日 星期四

Node.js筆記:Blog system(1)

練習利用Node.js實作一個Blog:

主要可以分成下列幾項下去實作.
1. 使用Express Web Framework建置一個網站
2. 使用cookie-based session建立會員機制
3. 新增Jade模板動態產生各個頁面
4. 新建自行設計的Restful API處理自訂路由
5. 使用mongoose控制MongoDB資料庫

  1. 使用Express Web Framework建置一個網站
    Express 4.0後提供了一個新的工具“express-generator”, 可以讓我們快速建立一個網站專案, 並幫我們初始化一些基礎設定.
    $npm install express
    $sudo npm install -g express-generator

enter image description here

    $express blog-system

執行express指令時, 後方參數為資料夾名稱.

enter image description here

express命令預設會把express和jade套件填入預先產生的package.json檔中, 所以我們能透過npm install指令把模組安裝完畢.

enter image description here

express 跟 Jade安裝好後就能透過Node.js啟動網站.

    $SET DEBUG=blog-system:* & npm start

打開瀏覽器輸入:http://localhost:3000就可以看到網站了.
enter image description here

npm init指令

Express產生的package.json只有基本的資訊, 這時可利用npm init指令更新專案的相關資訊.

enter image description here

當初入完自訂的資訊之後, 最後會把結果印出來(礙於畫面太長我只截取一小段表示), 如果確定資料正確就會存入package.json中.

安裝mongoose套件

在這邊我們要來修改package.json, 把mongoose套件寫到相依模組(dependencies), 這樣才能把文章寫入MongoDB或是從MongoDB中把文章讀取出來.

新增:
cookie-session是為了用來處理會員登入和登出時可以記得當下的status.

"*"表示版本不限!!!

    "cookie-session": "*",
    "mongoose": "*"

enter image description here

接著透過”npm install”來安裝新增的套件.
enter image description here

2016年6月28日 星期二

Node.js筆記:Express

Express Web Framework

官方定義:
Web Applications

Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

主要幫忙解決許多node.js http server 所需要的基本服務,讓開發http service 變得更為容易,不需要像之前需要透過層層模組(module)才有辦法開始編寫自己的程式。

使用Express之後, 它包裝了大多數繁複的HTTP低階操作, 使得我們的程式碼更為精簡.

安裝:
要不要-g(global)就看個人需求.

    npm install -g express

enter image description here

Why use Express?

  1. Express helps you respond to request with route support so that you may write responses to specific URLs.
  2. Supports multiple templating engines to simplify generating HTML.

Explanation of Routes

  • A router maps HTTP requests to a callback(針對從 Client 送來的 URL 做相對應的處理)
  • HTTP requests can be sent as GET/POST/PUT/DELETE …
  • URLs describe the location targeted.
  • To a request handler(callback)
    -> app.<HTTP請求類型>(url,handler{});
    app.get('/index', function(req, res){});

request: 連線要求的資訊和方法:
包含了瀏覽器傳來的各種信息,像是query ,body ,headers,都可以通過req對象訪問到。

response: 回應連線的資訊和方法
我們一般不從裡面取信息,而是通過它來定制我們向瀏覽器輸出的信息,比如 header 信息,比如想要向瀏覽器輸出的內容。這裡我們調用了它的 #send 方法,向瀏覽器輸出一個字符串。

app.get 可以帶入兩個參數,第一個是路徑名稱設定,第二個為Call back function),這裡面就如同前一篇介紹的 createServer 方法,裡面包含 request跟response 兩個物件。使用者就可以透過瀏覽器,輸入不同的url 切換到不同的頁面,顯示不同的結果。

Example:

var express = require('express');
var app = express();

app.get('/', function(req, res){
  res.send('This is GET method');
  res.end;
});

app.listen(process.env.PORT || 12345);

Express middleware

Express在處理一個連線要求時, 利用了middleware的設計, 讓開發者隨時可以自訂或擴充處理的流程. 也可以在適當的時機安插執行一些程式.

為了載入middleware要使用.use, 一個基本middleware長的樣子如下:

function logger(req, res, next){
  console.log(new Date(), req.method, req.url);
  next();
}

Middleware 會收到 3 個參數:
1. req 是 Request 物件,存放這此請求的所有資訊
2. res 是 Response 物件,用來回應該請求
3. next 用來控制流程

上面所寫的logger middleware會把每次request的時間, method還有url都列印在終端機上, 然後呼叫next()往下個middleware過去。Express.js的任務, 就是管理你所有middleware chain。

app.use 和 app.get

再來我們寫一個叫作hello的middleware:

function hello(req, res, next){
  res.write('Hello! \n');
  next();
}

將這個middleware加到我們的middlware chain中:

app.use(logger);
app.get('/hello', hello);
`app.use`表示 所有的request都會執行這個middleware
`app.get`表示 只有針對該URL 做 GET request的時候才會執行這個middleware

Reference:
1. http://expressjs.com
2. https://www.youtube.com/watch?v=czmulJ9NBP0
3. https://dca.gitbooks.io/nodejs-tw-wiki-book/content/book/node_express/node_express.html
4. https://stormpath.com/blog/how-to-write-middleware-for-express-apps
5. http://www.jollen.org/blog/2013/11/expressjs-middleware.html

2016年6月25日 星期六

Node.js筆記:Basic HTTP server

一個基本的網站伺服器流程:
1. 建立HTTP伺服器, 監聽指定的PORT
2. 等待連線
3. 取得新的客戶端連線要求
4. 回傳字串

Example:

var http = require('http');

var server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World!\n');
});

server.listen(12345);

console.log('Server rinning at http:127.0.0.1:88888/');

enter image description here

上述主要是透過Node內建的http模組, 然後使用http.createServer()去建立伺服器. 最後使用listen()方法去監聽指定的port.

當在cmd中執行上述的程式, 就可以在瀏覽器上輸入127.0.0.1:88888, 就能得到hellow world的回傳結果.

Port

Port:通過端口來區分出同一電腦內不同應用或者進程,從而實現一條物理網線(通過分組交換技術-比如internet)同時鏈接多個程序

端口號是一個16位的uint, 所以其範圍為1 to 65535 (對TCP來說, port 0 被保留,不能被使用. 對於UDP來說, source端的端口號是可選的, 為0時表示無端口).

server.listen(12345),程式在執行時,電腦接收到的12345端口的網路消息就會被發送給我們啟動的這個程式.

2016年6月23日 星期四

Django筆記:自定義過濾器

在這邊只需要記住一點“過濾器本身是一個函數”.

接著要新增一個filter來判斷食物辣或是不辣.

<tr>
    <td> {{ food.name }} </td>
    <td> {{ food.price }} </td>
    <td> {{ food.is_spicy|yes_no:"辣/不辣" }} </td>
    <td> {{ food.comment }} </td>
</tr>

1. 寫一個Filter函式

首先打開myfilters.py

def yes_no(bool_value, show_str):
    if bool_value:
        return show_str.partition('/')[0]
    else:
        return show_str.partition('/')[2]

這邊的函式名稱不需要跟我們定義的過濾器名稱一樣. 但是一樣的話通常會比較好管理.
bool_value是過濾器的第一個參數,他負責接收模板中pipe符號(|)左邊的值;而show_str是第二個參數,會用來接收過濾器中的額外參數, 在這邊指的是”辣/不辣”

2. 註冊該函式

from django import template
...
register = template.Library()
register.filter('yes_no', yes_no)

在這邊我們使用register的filter函式來幫我們註冊, filter函式第一個變數是”過濾器名稱“, 第二個參數是他對應的過濾器函式

3. 載入過濾器

最後就是在模板中載入他

{% load myfilters %}
...

2016年6月22日 星期三

Django筆記:模板繼承

當我們網站模板太多, 而且每一個模板之間共同的部分又很多的時候. 為了一個Don’t repeat yourself的精神. 就可以利用模板繼承的方式把“相似度”高的頁面共同使用同一個模板,方便我們之後的開發.

一個繼承的模板區塊以{% block BLOCKNAME %}為開頭, 以{% endblock %}為結尾. 可以填入內容也可以空白. 接著使用{% extends%}來繼承模板.

原則

  1. 基礎模板中的block越多越好, 並不是要每個區塊都要求繼承. 但是預留更多可變的位置, 會使得模板設計上更靈活
  2. 如果有大量重複的代碼存在各個模板中, 可以考慮抽取出來做成基礎模板.

模板繼承的步驟

  1. 定義基礎模板, 利用{% block BLOCKNAME %}和{% endblock %}.
  2. 利用 {% extends%}來繼承模板
  3. 利用模板區塊來覆寫”基礎模板“的內容, 未被覆寫的將用原本模板就設定好的內容.

Example:

#base.html

<!doctype html>
<html>
    <head>
        <title> {% block title %} {% endblock %} </title> 
    </head>
    <body>
        <nav>
            {% if request.user.is_authenticated %}
                <li><a href="#">Hi! {{ request.user }}</a></li>
                <li><a href="/restaurants_list/">餐廳列表</a></li>
                <li><a href="/accounts/logout/">登出</a></li>
            {% else %}
                <li><a href="/accounts/login/">登入</a></li>
                <li><a href="/accounts/register/">註冊</a></li>
             {% endif %}
        </nav>
        <h2>{% block h2 %}{% endblock %}</h2>
        {% block content %}{% endblock %}
    </body>
</html>
#index.html
{% extends "base.html" %}

{% block title %} 首頁 {% endblock %}

{% block content %}
    <h2>歡迎來到Dinbendon</h2>
{% endblock %}

2016年5月26日 星期四

筆記:iTerm2使用Oh-My-Zsh

改成使用Zsh的最大原因是:美觀
雖然網路上也有很多心得分享.

1.先刪除原本的設定檔.
由於之前我有使用其他的設定檔, 先用ls來搜尋目標資料夾, 再利用rm指令把檔案刪除就可以回覆成初始設定.

2.安裝Zsh

$ curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh

3.選擇主題
安裝完之後,可以對主題/套件做一些調整,Oh-My-Zsh 的主題可以參考

主題/套件主要的設定在~/.zshrc

$ vim ~/.zshrc

修改的內容如下:

ZSH_THEME = "agnoster"
# 將 ZSH_THEME 設為主題的名稱,以 agnoster 主題為例
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
# 中文設定的話要加上這兩行

4.調整字型
當完成主題設定重開iTerm時會顏色區塊中有出現“?”.
所以這裡就必須要調整字體的設定. 先下載背景顏色以及字體 選擇powerline字體

到 iTerm 的設定, 選擇 Profiles -> Colors -> Load Presets,選擇 Solarized Dark

enter image description here

點擊安裝完字體後, iTerm -> Preferences -> Profiles -> Text -> Change Font
兩個字體都選擇Change Font, 選擇下載的[Menlo for Powerline]即可.

enter image description here

如果用其他主題的話字體不必調整,請直接按照該主題的設定用就好了.

5.成果
enter image description here

參考網址:
1. http://v123582.github.io/blog/2015/12/06/MacOS-%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A/
2. https://gist.github.com/kevin-smets/8568070
3. http://iphone4.tw/forums/showthread.php?t=206652