有關 HTTP Header Content-Type

引言

當我們對 Server 發出 request 的時候
需要註明你 request 的 Content-Type
以下簡單介紹一下這些格式

測試工具

PostMan這套工具
可以模擬不同的格式資料發動 request 到你的 Server


Content-Type

application/x-www-form-urlencoded

常用的 Content-Type,簡單說就是 KEY-VALUE 的方式
如下, KEY firstname 的值是 marsen
lastname 是由使用者輸入

1
2
3
4
5
6
<form>
First name:<br />
<input type="text" name="firstname" value="marsen" /><br />
Last name:<br />
<input type="text" name="lastname" />
</form>

同時資料會作一次 url encoded,
產生類似下列的資料
firstname=marsen&lastname=lin&key%5b1%5d=value%5b1%5d


multipart/form-data

PostMan 中的選項 binary 其實就是包成這種格式
上傳檔案會使用這種 Content-Type,
這通常表示你的 html element 包含有 <input type="file">


其它

PostMan 中的選項 raw,可以用字串組合成任意 Content-Type,
參考Content-Type Table

  • application/json
    目前主流的 API 會用輕巧的 JSON 作為傳遞資訊的媒介
  • text/xmlapplication/xml
    早期標準的 Web 服務通常會透過 xml 作為交換資訊的媒介
  • text/plain
    有些 email 或 debug 的情況會使用 text/plain 作為 Content-Type,但是一般的 Request 情況不建議使用
  • 更多請參考Spec

參考

(fin)

[記錄]PowerShell 初體驗

需求

  1. 將指定的 Log 記錄,匯入資料庫,產生 row data
  2. 將 row data 轉換成為需要的報表資料
  3. 產生報表

規劃

  1. powershell 讀取檔案
  2. powershell 連接資料庫
  3. powershell 執行 SQL
  4. powershell 作 BulkInsert
  5. powershell 寫入檔案

PowerShell

簡記要點

  • powershell 可以直接取用 .Net Framework 或 COM 元件
  • 宣告變用要用$字號
  • # 是註解

讀取檔案

1
2
3
4
5
6
#用New-Object 建立.Net StreamReader 物件
$reader = New-Object System.io.streamReader(get-item $filePath)
#使用`[]`建立靜態類別讀取檔案
$file = [System.IO.File]::ReadAllLines($filePath)
#直接使用Get-Content讀取文檔
$file = Get-Content "C:\filepath\file"

連線資料庫與執行語法

1
2
3
4
5
$connection = New-Object System.Data.SQLClient.SQLConnection
$connection.ConnectionString = "server='$server';database='$database';uid='$user'; pwd='$pwd';Integrated Security=False;"
$connection.Open()
# do something
$connection.Close()

BulkInsert

  • 從檔案建立 DataTable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$table = New-Object System.Data.DataTable
#建立欄位
$col_title = New-Object system.Data.DataColumn "Title",([string])
$table.Columns.Add($col_title);
$col_content = New-Object system.Data.DataColumn "Content",([string])
$table.Columns.Add($col_content);
$col_author = New-Object system.Data.DataColumn "Author",([string])
$table.Columns.Add($col_author);
#建立資料
foreach($file in $files){
$dr = $table.NewRow();
$dr["Title"] = $file["title"]
$dr["Content"] = $file["content"]
$dr["Author"] = $file["author"]
}
#寫入資料表
$table.Rows.Add($dr);
  • 透過 BulkCopy 將 DataTable 寫入資料庫
1
2
3
4
5
$connection.Open()
$bulkCopy = New-Object (“Data.SqlClient.SqlBulkCopy”) -ArgumentList $connection
$bulkCopy.DestinationTableName = "tablename"
$bulkCopy.WriteToServer($datatable)
$connection.Close()

進度條

1
Write-Progress -Activity "BulkInsert" -Status "載入百分比: 100 %" -PercentComplete 100;

產生報表

1
$datatable | export-csv C:\Reports\20161026.csv -Encoding UTF8

參考

  1. https://msdn.microsoft.com/en-us/powershell
  2. https://msdn.microsoft.com/en-us/powershell/scripting/getting-started/cookbooks/using-static-classes-and-methods
  3. https://cmatskas.com/execute-sql-query-with-powershell/
  4. https://blogs.technet.microsoft.com/heyscriptingguy/

(fin)

[KATA] 用 TypeScript 作一個簡易的 TodoList (二) - 用JQuery實作

設計理念

  1. 顯示/新增/刪除 TodoList
  2. TodoList 會是一堆 todoItem 的集合,所以要定義 todoItem 的形別
    • Content : todoItem 的內容
    • Status : todoItem 的狀態,完成(done)、未完成(undo) ,設計成列舉
  3. 主要的功能
    • 建立 todoItem
    • 完成 todoItem
    • 繪製 todoList 到前端的畫面上

自我分析

跟 UI 耦合太高,Render 應該與 TodoService 分離 ,
DOM 註冊事件相依在 Service 裡面要抽離也不好抽 。
沒有先寫測試 , 要想一想怎麼與 UI 層作隔離。

程式碼

建立 BaseService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BaseService<T> {
constructor(private type: string) {}

List: Array<T>;

Create(data: T) {
this.List.push(data);
}

Delete(data: T) {
var index = this.List.indexOf(data);
this.List.splice(index, 1);
}

Render() {}
}

建立 todoItem interface

1
2
3
4
interface todoItem {
Content: string;
Status: todoStatus;
}

建立 todoItem Status 列舉

1
2
3
4
enum todoStatus {
undo,
done,
}

建立 TodoService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class TodoService extends BaseService<todoItem> {
constructor() {
super("todoItem");
}

public Render(): void {
let doneHtml = "";
let undoHtml = "";

this.List.forEach((item) => {
if (item.Status == todoStatus.done) {
doneHtml += `<li>${item.Content}<button class="recover-item btn btn-default btn-xs pull-right"><span class="glyphicon glyphicon-share-alt"></span></button><button class="remove-item btn btn-default btn-xs pull-right"><span class="glyphicon glyphicon-remove"></span></button></li>`;
} else if (item.Status == todoStatus.undo) {
undoHtml += `<li class="ui-state-default"><div class="checkbox"><label><input type="checkbox" value="" />${item.Content}</label></div></li>`;
}
});
$("#done-items").html(doneHtml);
$("#sortable").html(undoHtml);
$(".add-todo").val("");
}

Delete(data: todoItem) {
data.Status = todoStatus.done;
}

Init() {
//// todoList in localStorage
var list = window.localStorage.getItem("todoList");
if (!list) {
this.List = new Array<todoItem>();
} else {
this.List = JSON.parse(list);
}
window.onbeforeunload = (evt) => {
window.localStorage.setItem("todoList", JSON.stringify(this.List));
};

// mark task as done
$(".todolist").on(
"change",
'#sortable li input[type="checkbox"]',
(evt) => {
var self = evt.target;
var text = $(self).parent().text();
if ($(self).prop("checked")) {
var doneItem = this.List.filter((i) => {
return text == i.Content;
})[0];
this.Delete(doneItem);
this.Render();
}
}
);

$(".add-todo").on("keypress", (evt) => {
evt.preventDefault;
if (evt.which == 13) {
if ($(evt.target).val() != "") {
var todo = $(evt.target).val();
this.Create({
Content: $(evt.target).val(),
Status: todoStatus.undo,
});
this.Render();
} else {
// some validation
}
}
});

$(".todolist").on("click", "#done-items li button.recover-item", (evt) => {
var text = $(evt.target).parent().parent().text();
var recoverItem = this.List.filter((i) => {
return text == i.Content;
})[0];
recoverItem.Status = todoStatus.undo;
this.Render();
});

$(".todolist").on("click", "#done-items li button.remove-item", (evt) => {
var text = $(evt.target).parent().parent().text();
var removeItem = this.List.filter((i) => {
return text == i.Content;
})[0];
var index = this.List.indexOf(removeItem);
this.List.splice(index, 1);
this.Render();
});

$("#checkAll").on("click", (evt) => {
this.List.forEach((item) => {
item.Status = todoStatus.done;
});
this.Render();
});

//// Render
this.Render();
}
}

使用建立好的 TodoService

1
2
var todoService = new TodoService();
todoService.Init();

(fin)

[KATA] 用 TypeScript 作一個簡易的 TodoList (一) - 前置作業

目標

  1. 使用 typescript 開發
  2. 顯示/新增/刪除 TodoList

功能分析

  1. 只是練習,故不開發 server side 的程式
  2. 暫時存在 cookie 上
  3. 用 bootstrap 作簡單的樣式

實作記錄

UI : 使用 Bootstrap 沒有必要重新打造輪子,能用的就拿來用

  • 找到一個 TodoList 的樣版,內含 HTML 、 CSS 與 JS ,功能完整.
  • 取用樣版的 HTML.
  • 引用 bootstrap 3.3.5 CDN 上的 css .
  • 建立一個 todo.css 直接引用樣版的 css 並加入頁面參考.

開發環境

  1. 安裝 typescript
    npm install typescript --save
  2. 安裝 gulp
    npm install gulp
    npm install --global gulp
  3. 安裝 gulp-typescript
    npm install gulp-typescript
  4. 建立 gulpfile.js
1
2
3
4
5
6
7
8
var gulp = require("gulp");
var tsc = require("gulp-typescript");
gulp.task("default", function () {
return gulp
.src("public/javascripts/**/*.ts")
.pipe(tsc())
.pipe(gulp.dest("public/javascripts/"));
});

Typescript

  1. 關於 typescript 的定義檔, 以前有 tsd 與 typings 兩種管理工具,現在可以更簡便的合併到 npm 作管理 .

  2. 透過 TypeSearch 可以找到 bootstrap 的 typescript 定義檔.

  3. 執行 npm install --save @types/bootstrap 安裝 bootstrap (目前的版本是 Bootstrap 3.3.5) , 因為相依於 jquery 所以也會一併安裝

  4. 安裝 jquery-ui 的定義檔
    npm install --save @types/jqueryui

  5. 新增檔案 todo.ts ,將 樣版 的 javascript 複製貼上 .
    *註:因為 typescript 是 javascript 的 superset , 完全可以相容原生 javascript, 如果有任何錯誤, TypeScript 將會提示你

  6. todo.ts 引用 jquery 、jquery-ui 與 bootstrap 的 typescript 定義檔.

    1
    2
    3
    /// <reference path="../../../node_modules/@types/jquery/index.d.ts" /> ///
    <reference path="../../../node_modules/@types/bootstrap/index.d.ts" /> ///
    <reference path="../../../node_modules/@types/jqueryui/index.d.ts" />
  7. 頁面載入對應的 js 檔,記得放置順序 jquery 要在最前面,並將放在<\body>之後

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script
    src="http://code.jquery.com/jquery-2.2.4.min.js"
    integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
    crossorigin="anonymous"
    ></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script
    src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
    integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
    crossorigin="anonymous"
    ></script>
  8. 執行 gulp ,會產生 todo.js

  9. 頁面載入 js 檔,記得要放在相依的 js( jquery 、 bootstrap 、 jqueryui)之後.

1
<script type="text/javascript" src="/javascripts/kata/todo.js"></script>

截至目前為止,僅僅是在作複製樣版,同時一併處理一些基本 gulp 與 typescript 相關的配置
接下來才要開始寫 typescript

(待續)

參考

  1. 關於 TypeScript 2.0 之後的模組定義檔 ( Declaration Files ) ( *.d.ts )
  2. bootsnipp
  3. TYPESCRIPT + EXPRESS + NODE.JS

(fin)

[實作筆記] 怎麼建立一個網站?(三) - 簡單讓網站升級使用HTTP/2

應該知道的事

  • HTTP/1.1 是目前被應用得最廣泛的通訊協定,也可以說是最成功的一個
    • HTTP/1.1 基於 TCP,需要三方交握。
    • HTTP/1.1 本身存在得需多問題,安全性、效率等…。
  • HTTP/2 可以有效改善 HTTP/1.1 的缺點,而且可以向下相容
    • 主流的瀏覽器都開始支援了。
    • 只要設定好 Web Server,Client 端看使用的瀏覽器支不支援
  • 一流公司制定標準,二流作品牌,三流賣技術,四流作產品
    • HTTP/2 是源自 Google 的 SPDY。
    • Google 下一代 QUIC 推動中。
  • HTTP/2 並不強制加密(HTTPS)
    • 在主流瀏覽器為了向下相容,保留了 80 port 的 HTTP/1.1 通訊協定,用 443 port 實作 HTTP/2。
    • 所以還是要走 HTTPS,所以需要安全性憑証。
    • 安全性憑証要錢(現在也有免費的)。
    • cloudflare提供免費的安全性憑証。

前置作業

  1. 你要有cloudflare帳號。
  2. 看過我之網站的設定,簡單說就是一個 github page 與 透過 OpenShift 這個 PaaS 服務建立的網站。

配置

  1. cloudflare 設定 DNS,在指定的 domain 下,設定為「DNS and HTTP Proxy(CDN)」。
    設定為「DNS and HTTP Proxy(CDN)」
  2. 設定 Page-Rules,這個是為了開啟 SSL 1. 設定SSLFlexible,輸入*.username.com/*
    這個時候你就可以瀏覽https://blog.username.com/,但同時也可以瀏覽http://blog.username.com/
    我希望 HTTP 能自動跳轉 HTTPS,這部份如果是 Node 建立的網站,我可以用程式作轉導,
    但是如果是 github page 建立網站,就要再仰賴 cloudflare 了。
  3. 設定 Page-Rules,這個是為了開啟 SSL 1. 設定ALWAYS USE HTTPS,輸入http://*.username.com/*
    設定為「DNS and HTTP Proxy(CDN)」

設定完大致如下,順利的話幾分鐘就生效了。
生效

這時候瀏覽 http://blog.username.com/ 就會轉導到 https://blog.username.com/ 了 。

檢驗

使用 support HTTP/2 的瀏覽器,瀏覽網頁,觀察其 protocol 。
HTTP/2

系列文章

參考

(fin)

[工作筆記] 匯入文字資料到 MsSQL

前置作業

  1. 一個有權限寫入的 MsSQL Server 與 SSMS 管理工具
  2. 準備好你的檔案資料(Row Data)
  3. 這份記錄僅供參考(DB 版本使用 Microsoft SQL Server 2014)

步驟記錄

  1. 開啟 SSMS,連線測試 SQL Server
  2. 對測試資料庫右鍵>工作>匯入資料,開啟「SQL Server 匯入匯出精靈」
  3. 「開始畫面」> 下一步
  4. 「資料來源」選擇一般檔案來源,「檔案名稱」選取 Row Data 的檔案路徑
  5. 請依實際狀況選擇下列欄位
    • 「略過的標頭資料列數」: 預設為 0,可透過設定此欄位略過 Row Data 內含註解,標頭等資料
    • 「第一個資料列的資料行名稱」:若 Row Data 第一行為標頭,請勾選此欄位
    • 左側選單選擇「資料行」>「資料列分隔符號」選擇「{CR}{LF}」、「資料行分隔符號」選擇「Tab 鍵{t}」
  6. 左側選單選擇「預覽」確認匯入的資料無誤後,點擊下一步
  7. 「目的地」請選擇「SqlServer」,設定 ConnectionString
    • Data Source=1**.*..;Initial Catalog=TestDB;Persist Security Info=True;User ID=tester;Password=******;
  8. 「選取來源資料表和檢視畫面」確認無誤後,點擊下一步。
  9. 「檢閱資料類型對應畫面」請忽略資料類型的警告,點擊下一步。
  10. 「執行封裝畫面」勾選「立即執行」,點擊下一步。
  11. 「完成精靈畫面」點選完成。
  12. 執行時間長短,依 Row Data 大小而有所差異。

(fin)

[實作筆記] 怎麼建立一個網站?(二) - 簡單用github page 建立靜態網站

前置作業

  • 你要有一個 GitHub 帳號

建立 github page

如果不排斥看原文,可以直接參考

  1. 建立一個 Repository , 並且命名為 username.github.io , 這裡的 username 請使用你的 GitHub 帳號的 username.

  2. clone username.github.io 到你的本機上.

    1
    git clone https://github.com/username/username.github.io
  3. 建立一個靜態網頁 index.html , 隨便打點什麼。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html>
    <head>
    <title>Hello world</title>
    </head>
    <body>
    <h1>Hello world</h1>
    <p>This is my github page</p>
    </body>
    </html>
  4. commit 之後,push 到 github 上 > git add –all > git commit -m “Initial commit” > git push -u origin master

  5. 瀏覽 http://username.github.io 即完成

使用自訂的 Domain

  1. 首先準備好一個 domain ex: username.xyz
  2. 需要在根目錄底下,放入一個 CName file
    檔案的內容只需要你的 domain 即可
    ex:
    blog.username.xyz
    username.xyz
    username.xyz
  3. 在 Name Servers (例如cloudflare)上設定 CNAME 到 github page,
    blog.username.xyz 綁定到 username.github.io
    ex:
TYPE NAME VALUE TTL
CName * username.xyz auto
CName blog username.github.io auto

更多請參考「購買網域到設定 DNS」.

透過 Hexo 部署

Hexo 基本概念可以參考官方中文文件 .

  1. 重點在於 _config.yml 的設定
    deploy:
    type: git
    repository: https://github.com/username/username.github.io
    branch: master

  2. 執行 hexo d 進行部署,這個動作會將 hexo 建立出來的靜態網站(html+css+javascript+圖片等…)部署到 github page 上。

系列文章

(fin)

如何讓 Google Analytics 追踪你的 Hexo Blog

前置作業

  1. 你要有 google 帳號,並申請好你的 google_analytics ID
  2. 這個記錄僅針對 Hexo 預設的 Theme 有作用,未來有改 Theme 的話,可能會需要手動加入,再寫文章補上

開啟_config.yml

  1. 確定一下你是使用預設的 theme landscape ## Themes
    theme: landscape
  2. 開啟 root/themes/landscape/_config.yml
  3. 找到以下的設定區段 # Miscellaneous
    google_analytics:
  4. 填入步驟 1. 中所取得 google_analytics ID
  5. 部署網站,完成!

(fin)

[實作筆記] 怎麼建立一個網站?(一) - 購買網域到設定DNS

前置作業

  1. 準備好你的 google 帳號。
  2. 可以連到美國的 VPN。google domain beta 台灣尚未開放
  3. 準備一張信用卡,狠狠的刷下去(挑對 domain 其實很便宜啦)
  4. 你的網站,什麼語言都可以,靜態的網頁也可以。
    *這裡我事先準備好了兩個網站,
    分別是用github pagenodejsexpress,實作有機會再作記錄。

設定 domain

  1. Github page 所建立的網站,會提供一組 domain 給你
    ex:myDomain.github.io

  2. OpenShift 建立的網站,一樣會提供一組 domain 給你
    ex:myDomain.rhcloud.com

  3. google domain 本身有提供 Name servers , 但是由於 typeA 的 Domain,
    必須指定公用 ip(家中有裝 HiNet ADSL 可以申請一組);但實際上我的兩個網站,並不需要我準備實體 IP,
    只需要使用 type CName 將我的 subdomain 指向原本服務的 domain 即可。
    ex:

    • blog.myDomain.me → myDomain.github.io
    • www.myDomain.me → myDomain.rhcloud.com
  4. github page 要注意的事情,

你需要在你的 github page 的 repo root 加入一個名叫CName的檔案,
檔案的內容很簡單, 只需要你的 domain 即可
ex:
blog.myDomain.me
myDomain.me

Bare CName

大多數的時候, CName 的設定就夠了;不過對於我來說,
我會希望可以直接使用我的頂級 domain,畢竟這樣網址可以更短一些,
myDomain.mewww.myDomain.me 更有感覺。

這裡受限於 google 的 Name servers , 頂級 domain 必為 TypeA須綁定 ip
這裡我們可以使用一個免費的服務cloudflare,來達成目的

  1. 註冊cloudflare

  2. 登入後add site,輸入你註冊的 domain
    ex:myDomain

  3. cloudflare 會提供你至少兩組 Name servers
    ex:
    carter.ns.cloudflare.com
    tina.ns.cloudflare.com

  4. 請先登入google domain beta設定 Name servers 到cloudflare

  5. 請依以下步驟設定

    • * → myDomain.me
    • blog.myDomain.me → myDomain.github.io
    • www.myDomain.me → myDomain.rhcloud.com
  6. 等待約數分鐘就 ok 啦

結語

前前後後查資料弄了一個禮拜,但是實際上設定大概 1~2 小時就搞定了。
對 domain 跟 ip 的相對關係與實務結合後更有體會了。

歡迎指教討論。

系列文章

(fin)

Please enable JavaScript to view the LikeCoin. :P