、GET/POST 請求數據
在 PHP 中,可以直接通過全局變量 $_GET 和 $_POST 快速獲取 GET/POST 請求數據,GET 請求數據主要是 URL 查詢字符串中包含的參數,以前面在線論壇項目的群組詳情頁為例:
http://localhost:8080/thread/read?id=adb02107-d7c3-4f27-4de4-b586f231380e
上述 URL 請求中的 id=adb02107-d7c3-4f27-4de4-b586f231380e 就屬于 GET 請求數據,也就是查詢字符串,而對于用戶登錄表單:
當用戶輸入注冊郵箱和密碼后點擊「登錄」按鈕,會將輸入框中的數據作為請求實體發送 POST 請求到服務端,執行登錄認證,這里的表單數據就是 POST 請求數據,如果我們查看表單請求路由的 HTTP 報文:
就會看到表單請求數據,也就是上文提到的 HTTP 請求實體。
當然根據對服務端資源的操作類型不同,可以進一步細分為 POST、PUT、PATCH、DELETE 等包含請求實體的請求方法,為了簡化模型,我們這里只討論 POST 請求,而且其他幾種請求方法也可以統一通過 POST 請求完成,通常只有在設計遵循 RESTful 風格的 API 接口時,才會嚴格劃分不同的請求方法,關于這一點,后面介紹 REST + JSON 接口時再詳細討論。
Go 語言中獲取用戶請求數據的方式要更復雜一些,Go 也為此提供多個不同的結構體幫助我們讀取不同請求類型的數據,首先,我們可以通過請求對象上的 Form 讀取所有 GET/POST 請求數據,在 handlers/post.go 中新增 EditPost 方法如下:
func EditPost(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Fprintln(w, r.Form)
}
需要注意的是,在通過 r.Form 獲取所有請求數據之前,必須要先通過 r.ParseForm() 解析所有請求數據,否則無法獲取數據。
在 routes/web.go 中新增一個路由:
WebRoute{
"UpdatePost",
"POST",
"/post/edit",
handlers.EditPost,
},
然后我們重啟 HTTP 服務器,在命令行通過 curl 發起一個包含不同類型請求數據的請求:
可以看到,打印結果表明 r.Form 是一個包含所有請求數據的字典類型(map),包含 URL 查詢字符串和 POST 請求數據,這是一個 POST 請求,如果是 GET 請求呢?我們為 EditPost定義一個 GET 請求路由:
WebRoute{
"EditPost",
"GET",
"/post/edit",
handlers.EditPost,
},
再重啟 HTTP 服務器,發起一個只包含查詢字符串的 GET 請求(默認是 GET 請求,不需要通過 -X GET 指定):
同樣可以打印出和 POST 請求完全一樣的結果。
因此,request 對象上的 Form 可以獲取所有請求參數,包括查詢字符串和請求實體,并且不限請求類型。如果你想要進一步要獲取指定的參數值,可以以索引方式獲取指定參數對應的值,也可以通過 Form 提供的 Get 方法,就像我們從一個普通字典類型獲取鍵值一樣:
id1 := r.Form["id"]
id2 := r.Form.Get("id")
fmt.Println(id1)
fmt.Println(id2)
只不過兩者的返回值類型不一樣,前者是一個字符串切片,后者是一個字符串值:
[1]
1
上面的結果同時返回了查詢字符串和請求實體,如果只想獲取請求實體(即 POST 表單中的數據),可以通過 PostForm 實現:
func EditPost(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
id := r.Form.Get("id")
fmt.Println("post id:", id)
fmt.Println("form data:", r.PostForm)
io.WriteString(w, "表單提交成功")
}
這樣一來,就只能獲取到 POST 數據了:
然后在 HTTP 服務器日志,可以看到如下答應結果:
可以看到 r.PostForm 返回的也是字典類型數據,數據格式和 r.Form 完全一致,并且這次只包含了 POST 表單請求數據,不包含 URL 查詢字符串,也就是說,通過 r.PostForm 只能獲取 POST 請求數據(請求實體數據),無法獲取 GET 請求數據(查詢字符串中的數據),你可以再次發起 GET 請求進行驗證:
這個時候,可以看到服務器打印日志之包含 id 信息,表單信息為空:
通過 PostForm 獲取具體參數值的方式和 Form 一樣,這里就不再贅述了:
title := r.PostForm.Get("title")
content := r.PostForm.Get("content")
實際上,我們在前面的在線論壇項目中,就是通過這個方式獲取表單請求數據的:
err := request.ParseForm()
user, err := models.UserByEmail(request.PostFormValue("email"))
最后,還可以通過 FormValue 和 PostFormValue 獲取用戶請求數據,使用它們的好處是不再需要單獨調用 ParseForm 對表單數據進行解析,不過使用這兩個方法的時候只能獲取特定請求數據,不能一次獲取所有請求數據:
func EditPost(w http.ResponseWriter, r *http.Request) {
fmt.Println("post id:", r.FormValue("id"))
fmt.Println("post title:", r.PostFormValue("title"))
fmt.Println("post title:", r.PostFormValue("content"))
io.WriteString(w, "表單提交成功")
}
FormValue/PostFormValue 的區別和 Form/PostForm 一樣,這里通過命名就可以看出來,前者可以獲取所有 GET/POST 請求數據(即查詢字符串和請求實體),后者只能獲取 POST 請求實體數據。
注:FormValue/PostFormValue 之所以不用顯式調用 ParseForm 解析請求數據,是因為底層對其進行了封裝,實際上還是要調用這個方法。
上面的示例默認都是基于 HTML 表單請求,對于客戶端提交的 JSON 格式數據,使用ParseForm 是無法解析并獲取數據的,因為 HTML 表單請求數據默認是通過 application/x-www-form-urlencoded 編碼的,而 JSON 請求數據通常是通過 application/json 編碼,ParseForm 只能解析通過 application/x-www-form-urlencoded 編碼的數據。
對于 JSON 請求數據的解析,目前我們可以通過上篇教程介紹的,讀取完整請求實體并進行 JSON 解碼實現,下面我們改寫 AddPost 方法實現如下:
type Post struct {
Title string `json:"title"`
Content string `json:"content"`
}
func AddPost(w http.ResponseWriter, r *http.Request) {
len := r.ContentLength // 獲取請求實體長度
body := make([]byte, len) // 創建存放請求實體的字節切片
r.Body.Read(body) // 調用 Read 方法讀取請求實體并將返回內容存放到上面創建的字節切片
// io.WriteString(w, string(body))
post := Post{}
json.Unmarshal(body, &post) // 對讀取的 JSON 數據進行解析
fmt.Fprintf(w, "%#v\n", post) // 格式化輸出結果
}
我們將讀取的請求實體數據通過 JSON 解碼映射到 Post 結構體對象并將其輸出到響應結果。
重啟 HTTP 服務器,通過 curl 模擬客戶端提交 JSON 請求數據:
我們通過 -H 選項指定請求數據編碼格式為 application/json,然后請求數據調整為 JSON 格式字符串,最后通過輸出結果可以看到在服務端 JSON 請求數據已經可以成功解析并獲取。
到這里,我們已經了解了在 Go Web 編程中,常見的用戶請求數據如何解析并獲取(URL 查詢字符串、POST 表單數據、JSON 請求數據),實際上,和 PHP 中的 $_GET 和 $_POST 類似,Go 也是將 HTTP 請求數據映射到請求對象對應的結構體,然后開發者可以從上下文請求對象中解析并讀取這些請求數據,使用這些封裝好的對象的好處是它們屏蔽了底層的細節,統一了數據格式,可以大大提高開發效率,減少不必要的數據格式兼容成本。
下篇教程,學院君將給大家介紹如何從表單請求中獲取文件數據,并實現簡單的文件上傳功能。
(全文完)
單是PHP程序中最常使用的收集站點訪問者信息的數據輸入界面。通過表單瀏覽器獲取用戶的輸入數據,并傳送給Web服務器的腳本程序中,以各種不同的方式進行處理。在表單中提供了多種輸入方式,包括文本輸入域、單選或多選按鈕、下拉式列表域等。表單是網頁上由<form>標簽定義的一個特定區域,而表單的各種輸入域可以由<input>、<select>和<textarea> 3個標簽定義。
1 表單標簽<form>
一個表單用<form></form>標簽來創建,即定義表單的開始和結束位置,在開始和結束標簽之間的一切定義都屬于表單的內容。單擊提交按鈕時,提交的也是表單范圍之內的內容。另外,在<form>標簽中需要攜帶表單的相關信息,例如處理表單的腳本程序的位置、提交表單的方法等。這些信息對于瀏覽者是不可見的,但對于處理表單卻有著決定性的作用。該標簽的常用屬性如表1所示。
表1 HTML表單標簽中常用的屬性
<form>標簽中必須加action屬性,并且不能為空。例如,<form action="login.php" method="post">。如果不需要使用action屬性,也必須定義:<form action="no">。
2 文本域和密碼域
在<form>標簽內定義的<input>標簽具有重要的地位,該標簽是單個標簽,沒有結束標記。<input type="">標簽用來定義一個用戶輸入區,用戶可以在其中輸入信息。<input>標簽中共提供了9種類型的輸入區域,具體是哪一種類型由type屬性來決定。文本和密碼輸入域是一個單行文本框,它們基本相似,唯一不同的是,用戶在密碼域中輸入的字符并不以原樣顯示,而是將每個字符用“*”代替。文本和密碼輸入域的基本語法格式如下:
<input type="text" name="field_name" value="field_value" size="n" maxlength="n"> <!-- 輸入域 -->
<input type="password" name="field_name" value="field_value" size="n" maxlength="n"> <!-- 密碼域 -->
這些屬性的含義如表2所示。
表2 HTML中<input>標簽的常用屬性z
3 提交、重置和普通按鈕
在<input>標簽中,當type屬性值為“submit”時,表示這是一個提交按鈕,單擊提交按鈕后,可以實現表單內容的提交;當type屬性為“reset”時,表示這是一個重置按鈕,單擊重置按鈕后,表單的內容將恢復為默認值;當type屬性為“button”時,表示這是一個普通按鈕,并不實現任何功能,需要和JavaScript等腳本語言一起使用。button按鈕必須定義在form之間,否則Netscape瀏覽器不支持。這3種按鈕的基本語法格式如下:
<input type="submit" name="field_name" value="field_value"> <!-- 提交按鈕 -->
<input type="reset" name="field_name" value="field_value"> <!-- 重置按鈕 -->
<input type="button" name="field_name" value="field_value"> <!-- 普通按鈕 -->
4 單選按鈕和復選框
單選按鈕和復選框都有“選中”和“未選中”兩種狀態。同一組單選按鈕如果有多個選擇框,則選擇框之間是相互排斥的,只允許用戶選擇其中的一個。復選框和單選按鈕的區別是,復選框允許用戶同時選中同一表單中的多個或全部選項,當然,也可以只選其中的一個選項。它們都是只有在“選中”時,數據才能被提交到服務器端。其語法格式如下所示:
<input type="radio" name="field_name" value="field_value" checked> <!-- 單選按鈕 -->
<input type="checkbox" name="field_name" value="field_value" checked> <!-- 復選框 -->
在<input>標簽中,當type屬性值為“checkbox”時,表示這是一個復選框輸入域;當type屬性值為“radio”時,則表示這是一個單選按鈕輸入域。但在同一組中的多個單選按鈕名稱必須相同,它們之間才能相互排斥。單選按鈕和復選框都可以通過checked屬性設置為選中狀態。
.引用相關頭文件
引入CSS:
<link href="Scripts/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />
<link href="Scripts/ui.jqgrid.css" rel="stylesheet" type="text/css" />
引入JS:
<script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>
<script src="Scripts/jquery-ui.min.js" type="text/javascript"></script>
<script src="Scripts/grid.locale-en.js" type="text/javascript"></script>
<script src="Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>
因為jqGrid3.6及以后的版本集成了jQuery UI,所以,此處需要導入UI相關js和css。另外grid.locale-en.js這個語言文件必須在jquery.jqGrid.min.js之前加載,否則會出問題。
2.將jqgrid加入頁面中
根據jqGrid的文檔,要想生成一個jqGrid,最直接的方法就是:
$("#list").jqGrid(options);
其中list是頁面上的一個table:<table id="list"></table>
下面是一個簡單的例子:
<script type="text/javascript">
$(document).ready(function () {
jQuery("#list").jqGrid({
url: 'Handler.ashx',
datatype: "json",
mtype: 'GET',
colNames: ['SalesReasonID', 'Name', 'ReasonType', 'ModifiedDate'],
colModel: [
{ name: 'SalesReasonID', index: 'SalesReasonID', width: 40, align: "left", editable: true },
{ name: 'Name', index: 'Name', width: 100, align: "center" },
{ name: 'ReasonType', index: 'ReasonType', width: 100, align: "center" },
{ name: 'ModifiedDate', index: 'ModifiedDate', width: 150, align: "center", search: false }
],
rowList: [10, 20, 30],
sortname: 'SalesReasonID',
viewrecords: true,
sortorder: "desc",
jsonReader: {
root: "griddata",
total: "totalpages",
page: "currpage",
records: "totalrecords",
repeatitems: false
},
pager: jQuery('#pager'),
rowNum: 5,
altclass: 'altRowsColour',
//width: 'auto',
width: '500',
height: 'auto',
caption: "DemoGrid"
}).navGrid('#pager', { add: true, edit: true, del: true,search:false,refresh:false }); ;
})
二、 jqgrid的重要選項
具體的options參考,可以訪問jqGrid文檔關于option的章節(http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options)。其中有幾個是比較常用的,重點介紹一下:
2.1 prmNames選項
prmNames是jqGrid的一個重要選項,用于設置jqGrid將要向Server傳遞的參數名稱。其默認值為:
prmNames : {
page:"page", // 表示請求頁碼的參數名稱
rows:"rows", // 表示請求行數的參數名稱
sort: "sidx", // 表示用于排序的列名的參數名稱
order: "sord", // 表示采用的排序方式的參數名稱
search:"_search", // 表示是否是搜索請求的參數名稱
nd:"nd", // 表示已經發送請求的次數的參數名稱
id:"id", // 表示當在編輯數據模塊中發送數據時,使用的id的名稱
oper:"oper", // operation參數名稱
editoper:"edit", // 當在edit模式中提交數據時,操作的名稱
addoper:"add", // 當在add模式中提交數據時,操作的名稱
deloper:"del", // 當在delete模式中提交數據時,操作的名稱
subgridid:"id", // 當點擊以載入數據到子表時,傳遞的數據名稱
npage: null,
totalrows:"totalrows" // 表示需從Server得到總共多少行數據的參數名稱,參見jqGrid選項中的rowTotal
}
2.2 jsonReader選項
jsonReader是jqGrid的一個重要選項,用于設置如何解析從Server端發回來的json數據,如果Server返回的是xml數據,則對應的使用xmlReader來解析。jsonReader的默認值為:
jsonReader : {
root: "rows", // json中代表實際模型數據的入口
page: "page", // json中代表當前頁碼的數據
total: "total", // json中代表頁碼總數的數據
records: "records", // json中代表數據行總數的數據
repeatitems: true, // 如果設為false,則jqGrid在解析json時,會根據name來搜索對應的數據元素(即可以json中元素可以不按順序);而所使用的name是來自于colModel中的name設定。
cell: "cell",
id: "id",
userdata: "userdata",
subgrid: {
root:"rows",
repeatitems: true,
cell:"cell"
}
}
假如有下面一個json字符串:
{"totalpages":"3","currpage":"1","totalrecords":"11","griddata":[{"SalesReasonID":"1","Name":"Price","ReasonType":"Other","ModifiedDate":"1998年6月1日"},{"SalesReasonID":"2","Name":"On Promotion","ReasonType":"Promotion","ModifiedDate":"1998年6月1日"},{"SalesReasonID":"3","Name":"Magazine Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"},{"SalesReasonID":"4","Name":"Television Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"},{"SalesReasonID":"5","Name":"Manufacturer","ReasonType":"Other","ModifiedDate":"1998年6月1日"}]}
其對應的jsonReader為:jsonReader: {
root: "griddata",
total: "totalpages",
page: "currpage",
records: "totalrecords",
repeatitems: false
}
注:cell、id在repeatitems為true時可以用到,即每一個記錄是由一對id和cell組合而成,即可以適用另一種json結構。援引文檔中的例子:
repeatitems為true時:
jQuery("#gridid").jqGrid({
...
jsonReader : {
root:"invdata",
page: "currpage",
total: "totalpages",
records: "totalrecords"
},
...
});
json結構為:
{
"totalpages": "xxx",
"currpage": "yyy",
"totalrecords": "zzz",
"invdata" : [
{"id" :"1", "cell" :["cell11", "cell12", "cell13"]}, // cell中不需要各列的name,只要值就OK了,但是需要保持對應
{"id" :"2", "cell" :["cell21", "cell22", "cell23"]},
...
]
}
repeatitems為false時:
jQuery("#gridid").jqGrid({
...
jsonReader : {
root:"invdata",
page: "currpage",
total: "totalpages",
records: "totalrecords",
repeatitems: false,
id: "0"
},
...
});
json結構為:
{
"totalpages" : "xxx",
"currpage" : "yyy",
"totalrecords" : "zzz",
"invdata" : [
{"invid" : "1","invdate":"cell11", "amount" :"cell12", "tax" :"cell13", "total" :"1234", "note" :"somenote"}, // 數據中需要各列的name,但是可以不按列的順序
{"invid" : "2","invdate":"cell21", "amount" :"cell22", "tax" :"cell23", "total" :"2345", "note" :"some note"},
...
]
}
2.3 colModel的重要選項
colModel也有許多非常重要的選項,在使用搜索、排序等方面都會用到。這里先只說說最基本的。
三、 注意事項
1. 動態改變Add Form或者Edit Form中的select的內容,如:改變下圖中的Comparator下拉中的內容。
$("#list_d").navGrid('#pager_d',{add:true,edit:true,del:true,search:false,refresh:false},
{
checkOnSubmit:false, closeAfterEdit: true,recreateForm:true,
beforeInitData:function(formid){
initComparator();
},
beforeShowForm: function(formid){
$("#list_d").jqGrid('setColProp', 'Name', { editrules:{required:false},});
$('#tr_Name', formid).hide();
}
},//edit
{},//add
{}//del
)
beforeInitData, beforeShowForm在每次點擊編輯的時候都會執行。initComparator的作用是通過ajax獲取數據,然后利用$("#list_d").jqGrid('setColProp', 'Comparator', { editoptions: { value: valueString} });來設置Comparator下拉中的內容。其中valueString的格式如下’ equal to: equal to; not equal to: not equal to’。鍵值之間用冒號隔開,2項之間用分號隔開。注意:把recreateForm設為true,否則'setColProp'只在第一次調用時有效。
2. var rowNum=parseInt($(this).getGridParam("records"), 10); 得到數據條數。
3. jQuery("#list_d").clearGridData();清空數據。
4. jQuery("#list").getCell(ids,"Key");獲取第ids行的key列。
5. $("#list").jqGrid('setSelection', "1");選中第一行。放在loadComplete:中在gird加載完成的時候自動選中第一行。loadComplete:function(data){$("#list").jqGrid('setSelection', "1");
}
6. 對于像1中的可編輯的字段,可以設定rule,參見http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules#editrules
7. 修改Option,以URL為例
jQuery("#list_d").jqGrid('setGridParam',{url:"xxx.aspx",page:1}).trigger('reloadGrid');
*請認真填寫需求信息,我們會在24小時內與您取得聯系。