1. 程式人生 > >[Ext JS 6 By Example 翻譯] 第4章 - 資料包裝

[Ext JS 6 By Example 翻譯] 第4章 - 資料包裝

轉載自:http://www.jeeboot.com/archives/1222.html

 

本章探索 Ext JS 中處理資料可用的工具以及伺服器和客戶端之間的通訊。在本章結束時將寫一個呼叫 RESTful 服務的例子。下面是本章的內容:

  • 模型
  • Schema
  • Stores
  • 代理
  • 過濾和排序
  • 做一個基於 RESTful 的小專案

Model(模型)

一個模型包含欄位,欄位型別,校驗,關聯和代理。它是通過擴充套件 Ext.data.Model 類來定義的。

其中型別,校驗,關聯和代理都是可選的。 當一個欄位沒有指定型別時,將使用預設型別 ‘auto’。 通常 代理都是設定在 store 中,但是 model 裡也可以設定代理。

Field(欄位)

Ext.data.field.Field 用於新增模型的屬性。 這個屬性欄位的型別可以是預定義的或自定義型別。下列是可用的預定義型別:

  • auto
  • boolean
  • date
  • int
  • number
  • string

資料轉換

預設當你為一個欄位指定型別後,這個資料將在儲存到該欄位之前進行轉換。轉換的過程由內建的 convert 方法處理。auto 欄位型別不具備 convert 方法,所以 auto 型別的欄位不會發生轉換。

其他所有的欄位型別都有 convert 方法。如果你想避免其他欄位轉換來提高效能,你可以通過指定各自的轉換配置為 null 。如下列 Employee 模型所示。

驗證器/校驗器

資料模型支援資料的校驗。下列是支援的驗證器:

  • presence: 確保值是存在於一個欄位
  • format: 能夠用一個正則表示式來進行驗證
  • length: 支援最大和最小長度的驗證
  • exclusion 和 inclusion: 你可以通過給出一組值來確保欄位的值存在或不存在於這組值中。

下列程式碼示例展示一個 Employee 模型,在模型的欄位上使用驗證器:

 

 
  1. Ext.define('Employee', {

  2. extend: 'Ext.data.Model',

  3. fields: [

  4. { name: 'id', type: 'int', convert: null },

  5. { name: 'firstName', type: 'string' },

  6. { name: 'lastName', type: 'string' },

  7. { name: 'fulltime', type: 'boolean', defaultValue: true, convert: null },

  8. { name: 'gender', type: 'string' },

  9. { name: 'phoneNumber', type: 'string'},],

  10. validators: {

  11. firstName: [{

  12. type: 'presence'

  13. },{

  14. type: 'length',

  15. min: 2

  16. }],

  17. lastName:[{

  18. type: 'presence'

  19. },{

  20. type: 'length',

  21. min: 2

  22. }],

  23. phoneNumber: {

  24. type: 'format',

  25. matcher: '/^[(+{1})|(00{1})]+([0-9]){7,10}$/'

  26. },gender: {

  27. type: 'inclusion',

  28. list: ['Male', 'Female']

  29. }

  30. }

  31. });


 

 

建立一個模型的例項,使用 Ext.create ,如以下程式碼所示:

 

 
  1. var newEmployee = Ext.create('Employee', {

  2. id : 1,

  3. firstName : 'Shiva',

  4. lastName : 'Kumar',

  5. fulltime : true,

  6. gender: 'Male',

  7. phoneNumber: '123-456-7890'

  8. });


 

 

建立好的模型物件有 get 和 set 方法用來讀取和設定欄位值:

 

var lastName = newEmployee.get('lastName'); newEmployee.set('gender','Female');

 

 

關係

定義實體之間的關係,使用下列關係型別之一:

One-to-one(一對一)

下列程式碼代表一對一關係:

 

 
  1. Ext.define('Address', {

  2. extend: 'Ext.data.Model',

  3. fields: [

  4. 'address',

  5. 'city',

  6. 'state',

  7. 'zipcode']

  8. });

 
  1. Ext.define('Employee', {

  2. extend: 'Ext.data.Model',

  3. fields: [{

  4. name: 'addressId',

  5. reference: 'Address'

  6. }]

  7. });

 

 

One-to-many(一對多)

下列程式碼代表一對多關係:

 

 
  1. Ext.define('Department', {

  2. extend: 'Ext.data.Model',

  3. fields: [{

  4. name: 'employeeId',

  5. reference: 'Employee'

  6. }]

  7. });

 
  1. Ext.define('Division', {

  2. extend: 'Ext.data.Model',

  3. fields: [{

  4. name: 'departmentId',

  5. reference: 'Department'

  6. }]

  7. });

 

 

Many-to-many(多對多)

下列程式碼代表多對多關係:

 

 
  1. Ext.define('Employee', {

  2. extend: 'Ext.data.Model',

  3. fields: [{

  4. name: 'empId',

  5. type: 'int',

  6. convert: null

  7. },{

  8. name: 'firstName',

  9. type: 'string'

  10. },{

  11. name: 'lastName',

  12. type: 'string'

  13. }],

  14. manyToMany: 'Project'

  15. });

 
  1. Ext.define('Project', {

  2. extend: 'Ext.data.Model',

  3. fields: [

  4. 'name'

  5. ],

  6. manyToMany: 'Employee'

  7. });

 

 

自定義欄位型別

你也可以通過簡單的擴充套件 Ext.data.field.Field 類來建立自定義欄位型別。如以下程式碼所示:

 

 
  1. Ext.define('App.field.Email', {

  2. extend: 'Ext.data.field.Field',

  3. alias: 'data.field.email',

  4. validators: {

  5. type: 'format',

  6. matcher: /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,

  7. message: 'Wrong Email Format'

  8. }

  9. });

 

 

Store

一個 store 代表一個 模型的例項 的集合並用於代理獲取資料。store 還定義了集合操作,例如 排序,過濾等等。它是通過擴充套件 Ext.data.Store 定義的。

通常,當定義一個 store ,你需要為 store 指定一個 代理。這是一個配置屬性,它告訴 store 如何讀取和寫入資料。在本章的後面我們將會看到更多詳細的不同種類的代理。

 

下列程式碼是一個 store 的例子,它使用 RESTful API 請求載入為 JSON 格式資料:

 

 
  1. var myStore = Ext.create('Ext.data.Store', {

  2. model: 'Employee',

  3. storeId: 'mystore',

  4. proxy: {

  5. type: 'rest',

  6. url: '/employee',

  7. reader: {

  8. type: 'json',

  9. rootProperty: 'data'

  10. }

  11. },

  12. autoLoad: true,

  13. autoSync: true

  14. });


 

 

這裡 storeId 是 store 的唯一標示符。這個 store 有一個方法 load ,用於通過代理配置載入資料。如果你設定 autoLoad 為 true ,那麼當 store 建立後將會自動呼叫 load 方法。如果設定 autoLoad 為 false ,那麼你可以手動的呼叫 load 方法載入資料。

同樣的,你可以設定 autoSync 為 true ,當你對 store 的記錄進行修改新增和刪除時將會自動發生同步。

在前面的例子中,當在 store 中執行修改,新增或刪除時,它將呼叫 REST service API 。如果你設定這個屬性為 false ,那麼你可以呼叫 sync 方法執行同步操作。

呼叫 sync 方法將觸發一個批量操作。所以如果你新增和刪除了多條記錄,那麼呼叫 sync方法這個過程中將觸發多次伺服器呼叫。這是一個 sync 呼叫的例子:

 

 
  1. store.sync({

  2. callback: function (batch, options) {

  3. //Do something

  4. },

  5. success: function (batch, options) {

  6. //Do something

  7. },

  8. failure: function (batch, options) {

  9. //Do something

  10. },

  11. scope: this

  12. });


 

 

這裡,當所有 sync 操作全部完成並且沒有任何例外和失敗時呼叫 success 方法。如果有一個或多個操作在 sync 過程中失敗,將呼叫 failure 方法。callback 方法則會在同步操作完成後,不論成功失敗都會被呼叫。

如果 failure 被呼叫,你可以檢查 batch 異常陣列來看到什麼操作失敗,為什麼。這裡 options 是 sync 方法中傳遞的原始引數。

這個 sync 方法呼叫時也可以新增一個屬性 params ,它可以用於在同步的時候傳遞任意你想附加的引數。

 

內聯資料 store

如果你不想繫結 store 到伺服器或外部儲存,例如瀏覽器本地儲存,但還想使用一些特殊的的靜態資料,那麼可以直接硬編碼內聯資料到 store ,如以下程式碼所示:

 

 
  1. Ext.create('Ext.data.Store', {

  2. model: 'Employee',

  3. data: [{

  4. firstName: 'Shiva',

  5. lastName: 'Kumar',

  6. gender: 'Male',

  7. fulltime: true,

  8. phoneNumber: '123-456-7890'

  9. },{

  10. firstName: 'Vishwa',

  11. lastName: 'Anand',

  12. gender: 'Male',

  13. fulltime: true,

  14. phoneNumber: '123-456-7890'

  15. }]

  16. });


 

 

過濾和排序

store 支援本地或遠端的過濾和排序。下面是本地排序的例子:

 

 
  1. var myStore = Ext.create('Ext.data.Store', {

  2. model: 'Employee',

  3. sorters: [{

  4. property: 'firstName',

  5. direction: 'ASC'

  6. }, {

  7. property: 'fulltime',

  8. direction: 'DESC'

  9. }],

  10. filters: [{

  11. property: 'firstName',

  12. value: /an/

  13. }]

  14. });


 

 

使用遠端排序和遠端過濾,需要設定 remoteSort 和 remoteFilter 屬性為 true 。如果你設定為 true ,那麼在伺服器端你必須要執行過濾和排序併為客戶端返回過濾或排序後的資料。

 

訪問 store

你也許需要在應用的其他地方訪問這個 store 有很多方法可以用。

使用 StoreManager 訪問 store

使用 store 管理器的 lokkup 方法,你能夠在程式的任何地方來訪問 store。為此我們需要使用 storeId ,注意,當 store 通過一個控制器例項化的時設定 storeId,storeId 將會被覆蓋,這是一個例子:

 

 
  1. Ext.create('Ext.data.Store', {

  2. model: 'Employee',

  3. storeId: 'mystore',

  4. proxy: {

  5. type: 'rest',

  6. url: '/employee',

  7. reader: {

  8. type: 'json',

  9. rootProperty: 'data'

  10.  
  11. }

  12. }

  13. });


 

 

假設我們已經建立了 store 如上所示。現在你可以通過傳遞 storeId 到 store 管理器的 StoreManager.lookup 方法訪問這個 store,如以下程式碼所示:

 

Ext.data.StoreManager.lookup('myStore');


你也可以使用 Ext.getStore 方法。Ext.getStore 是 Ext.data.StoreManager.lookup 的快捷方法。

 

 

使用 Ext.app.ViewModel 訪問 store

你可以通過 Ext.app.ViewModel 的 getStore 方法 訪問 store 。當你是在 ViewController 中,最好使用這種方式,下面是程式碼示例:

 

var myStore = this.getViewModel().getStore('myStore')


這個 getStore 方法也定義在 view 中,你也可以用它訪問 store。

 

 

Store 事件

store 提供了很多可監聽的事件。store 的一些常用事件:

  • add: 當一條記錄新增到 store 後呼叫
  • beforeload: 在載入資料之前呼叫
  • beforesync: 在進行同步操作之前呼叫
  • datachanged: 當 store中的記錄產生新增或刪除時觸發呼叫
  • load: 當 store 讀取一個遠端資料來源後觸發呼叫
  • remove: 從 store 移除一條記錄觸發呼叫
  • update: 當 store 中的一條記錄被更新觸發呼叫

給出的一個 store 事件監聽的例子:

 

 
  1. Ext.create('Ext.data.Store', {

  2. model: 'Employee ',

  3. storeId: 'mystore',

  4. proxy: {

  5. type: 'rest',

  6. url: '/employee',

  7. reader: {

  8. type: 'json',

  9. rootProperty: 'data'

  10. }

  11. },

  12. listeners: {

  13. load: function (store, records, options) {

  14. //Do something

  15. }

  16. }

  17. });


 

 

如果你想在你的控制器中監聽 store 事件,你可以這樣做:

 

 
  1. init: function() {

  2. this.getViewModel().getStore('myStore').on('load', this.onStoreLoad, this);

  3. }


 

 

在 ViewModel 中定義 store

你可以分別定義 store 和 ViewModel 或者定義到一起。通常可取的定義 store 在 ViewModel 自身。一個例子如下:

 

 
  1. Ext.define('MyApp.view.employee.EmployeeModel', {

  2. extend: 'Ext.app.ViewModel',

  3. alias: 'viewmodel.employee',

  4. stores: {

  5. employee: {

  6. fields: [{

  7. name: 'id',

  8. type: 'string'

  9. },{

  10. name: 'firstname',

  11. type: 'string'

  12. },{

  13. name: 'lastname',

  14. type: 'string'

  15. }],

  16. autoLoad: false,

  17. sorters: [{

  18. property: 'firstname',

  19. direction: 'ASC'

  20. }],

  21. proxy: {

  22. type: 'rest',

  23. url: 'employee',

  24. reader: {

  25. type: 'json',

  26. },

  27. writer: {

  28. type: 'json'

  29. }

  30. }

  31. }

  32. }

  33. });


 

 

代理

所有的 stores 和 models 使用 proxy 來載入和儲存資料。在代理中使用這個配置你可以指定如何讀取和寫入資料。你也可以指定呼叫 URL 讀取資料;你可以告訴讀取器這些資料的格式,不論是 JSON 還是 XML ,等等。

有兩種型別的代理:客戶端代理和伺服器代理。

客戶端代理

客戶端代理是用於處理客戶端本身資料的載入和儲存。這氛圍三種客戶端代理:記憶體,本地儲存,和會話儲存。

記憶體代理

記憶體代理是用於記憶體中的區域性變數資料。下列程式碼展示了一個記憶體代理的例子。在這裡這個資料的值沒有硬編碼。只要是合適的格式,可以是任何變數的資料:

 

 
  1. var data = {

  2. data: [{

  3. firstName: 'Shiva',

  4. lastName: 'Kumar',

  5. gender: 'Male',

  6. fulltime: true,

  7. phoneNumber: '123-456-7890'

  8. },{

  9. firstName: 'Vishwa',

  10. lastName: 'Anand',

  11. gender: 'Male',

  12. fulltime: true,

  13. phoneNumber: '123-456-7890'

  14. }]

  15. };

  16. var myStore = Ext.create('Ext.data.Store', {

  17. model: 'Employee',

  18. data : data,

  19. proxy: {

  20. type: 'memory',

  21. reader: {

  22. type: 'json',

  23. rootProperty: 'Employee'

  24.  
  25. }

  26. }

  27. });


 

 

本地儲存代理

這個是用於訪問瀏覽器本地儲存。它是一個鍵值對儲存新增在 html5 ,所以需要瀏覽器支援:

 

 
  1. var myStore = Ext.create('Ext.data.Store', {

  2. model: 'Benefits',

  3. autoLoad: true,

  4. proxy: {

  5. type: 'localstorage',

  6. id: 'benefits'

  7. }

  8. });


 

 

會話儲存代理

這個是用於訪問瀏覽器會話儲存。這也是一個 html5 特性,因此需要比較現代的瀏覽器才能支援。這些資料是當 session 超時後會被銷燬:

 

 
  1. var myStore = Ext.create('Ext.data.Store', {

  2. model: 'Benefits',

  3. autoLoad: true,

  4. proxy: {

  5. type: 'localstorage',

  6. id : 'benefits'

  7. }

  8. });


 

 

 

伺服器端代理

伺服器端代理是向伺服器通訊來讀取或儲存資料。有四種代理:

  • Ajax: 用於非同步請求資料
  • Direct: 使用 Direct 與伺服器通訊
  • JSONP (JSON with padding): 這很有用,當你需要傳送跨域請求時。而 ajax 只能請求相同的域。
  • REST: 這會向伺服器傳送一個 ajax 請求,使用 RESTful 的風格,例如 GET,POST,PUT,和 DELETE。

在本章我們已經看到過一個 REST 代理的例子。讓我們瞧一瞧 JSONP 的例子:

 

 
  1. var myStore = Ext.create('Ext.data.Store', {

  2. model: 'Products',

  3. proxy: {

  4. type: 'jsonp',

  5. url : 'http://domain.com/products',

  6. callbackKey: 'productsCallback'

  7. }

  8. });


 

 

To do 待辦(RESTful 的示例專案)

現在,讓我們運用本章和前面章節所學內容,建立一個 To do 待辦 應用。這裡我們將使用 store 的 REST 代理來連線 REST 服務。

我們來建立一個簡單的 RESTful 服務,我們將使用 Go 語言,也稱為 Golang 。它由 Google 開發,是靜態語言,語法鬆散。

你不必學習 Go 語言,這個專案主要集中在 Ext JS 的教學。你可以用任意的你熟悉的語言替換 Go 語言開發的 RESTful 服務程式碼。

 

這是我們將要建立的應用的設計:

讓我們瞧一瞧這個專案的一些重要檔案。完整的程式碼在這裡 https://github.com/ananddayalan/extjs-by-example-todo

下面截圖展示了這個程式的目錄結構:

 

首先,讓我們先來建立 store ,我們已經在本章學習過了。這裡我將 store 寫在 ViewModel 中。

下列 ViewModel中有三個欄位:id ,desc(描述) ,done(顯示待辦是否完成),代理型別為 rest 並設定 URL 地址為 tasks 。因為是 REST 代理,它將會根據不同的操作訪問相應的 HTTP 服務。

例如,當你刪除一條記錄,這個服務請求 URL 將是 <base URL>/task/{id} 。如果你的應用部署在 localhost 埠為 9001 ,那麼請求 URL 就是 http://localhost:9001/tasks/23333 ,並且 HTTP verb 是 DELETE ,你可以理解為提交的 HTTP 動作型別。這裡 23333 是記錄的ID。當你新增一條記錄,URL 就是 http://localhost:9001/tasks ,這時 HTTP verb 就是 POST,同時要新增的記錄也會以 JSON 形式傳送給伺服器:

 

 
  1. Ext.define('ToDo.view.toDoList.ToDoListModel', {

  2. extend: 'Ext.app.ViewModel',

  3. alias: 'viewmodel.todoList',

  4. stores: {

  5. todos: {

  6. fields: [ {

  7. name: 'id',

  8. type: 'string'

  9. },{

  10. name: 'desc',

  11. type: 'string'

  12. }],

  13. autoLoad: true,

  14. sorters: [{

  15. property: 'done',

  16. direction: 'ASC'

  17. }],

  18. proxy: {

  19. type: 'rest',

  20. url: 'tasks',

  21. reader: {

  22. type: 'json',

  23. },

  24. writer: {

  25. type: 'json'

  26. }

  27. }

  28. }

  29. }

  30. });


 

 

現在我們來建立檢視,我們將建立一個 view 作為 To do 待辦 列表,一個文字框用於鍵入新的待辦,和一個新增按鈕。

這個 To do 待辦 列表的 UI 是基於 store 中的記錄,動態的建立的,並且每當一個記錄新增或移除,這個 view 都會相應的更新。Ext JS grid 元件可以實現我們的目的,但是你還沒有學習過這個元件,所以這裡我們不使用它來建立,而通過其他方式,你還將學習到如何處理自定義 UI。

動態建立 待辦列表 UI,我把程式碼寫在 ViewController 裡。在這個 view 中,我添加了 文字框 和 按鈕:

 

 
  1. Ext.define('ToDo.view.toDoList.ToDoList', {

  2. extend: 'Ext.panel.Panel',

  3.  
  4. /* Marks these are required classes to be to loaded before loading this view */

  5. requires: [

  6. 'ToDo.view.toDoList.ToDoListController',

  7. 'ToDo.view.toDoList.ToDoListModel'

  8. ],

  9. xtype: 'app-todoList',

  10. controller: 'todoList',

  11. /* View model of the view. */

  12. viewModel: {

  13. type: 'todoList'

  14. },

  15. items: [{

  16. xype: 'container',

  17. items: [{

  18. xtype: 'container',

  19. layout: 'hbox',

  20. cls: 'task-entry-panel',

  21. defaults: {

  22. flex: 1

  23. },

  24. items: [{

  25. reference: 'newToDo',

  26. xtype: 'textfield',

  27. emptyText: 'Enter a new todo here'

  28. },{

  29. xtype: 'button',

  30. name: 'addNewToDo',

  31. cls: 'btn-orange',

  32. text: 'Add',

  33. maxWidth: 50,

  34. handler: 'onAddToDo'

  35. }]

  36. }]

  37. }]

  38. });


 

 

上面的 view 程式碼裡,我指定了兩個 cls 屬性分別是 btn-orange 和 task-entry-panel 。這是 CSS 類,用於新增一些 CSS 樣式。在上面的 UI 截圖中,你可以看到 Add 按鈕並不是我們所用的主題(crisp)的預設顏色。因為我指定了 CSS 類來自定義這個按鈕了。

現在我們可以建立 ViewController 。我們將通過在初始化函式中讀取 store 中的記錄來建立 待辦列表 的UI 。這裡,一旦應用載入,我們將呼叫 store 的 load 方法。這將使得 rest 服務呼叫,從伺服器獲取記錄,並遍歷結果,每一條記錄我們都會建立一行在 UI 中。

提供刪除功能,我會在每一條記錄上新增一個刪除圖示。下列程式碼用於繫結這個圖示的點選事件。待辦記錄 UI 是動態生成的,所以我們需要事件委派,如以下程式碼所示:

 

 
  1. Ext.getBody().on('click', function (event, target) {

  2. me.onDelete(event, target);

  3. } , null, {delegate: '.fa-times' });


 

 

如果 UI 不是動態新增的,正常的繫結點選事件,如以下程式碼:

 

 
  1. Ext.select('fa-times').on('click', function (event, target) {

  2. me.onDelete(event, target);

  3. });

 

 

 

以下是檢視待辦列表(ToDoList)  的檢視控制器(ViewController)的程式碼。  onAddToDo 方法是新增按鈕的點選事件處理,方法裡通過 lookupReference 方法傳遞引用名稱設定到 ToDoList 檢視:

 

 
  1. Ext.define('ToDo.view.toDoList.ToDoListController', {

  2. extend: 'Ext.app.ViewController',

  3. alias: 'controller.todoList',

  4. views: ['ToDo.view.toDoList.ToDoList'],

  5. init: function () {

  6. var me = this;

  7. //Here we're calling the load method of the store and passing an anonymous method for the callback. So, the load will call the server to get the data first and calls the anonymous method. The anonymous method then, add the data to the view.

  8. this.getViewModel().data.todos.load(function (records) {

  9. Ext.each(records, function (record) {

  10. //Add a container for each record

  11. me.addToDoToView(record);

  12. });

  13.  
  14. });

  15.  
  16. Ext.getBody().on('click', function (event, target) {

  17. me.onDelete(event, target);

  18. }, null, {

  19. delegate: '.fa-times'

  20. });

  21. },

  22. onAddToDo: function () {

  23. var store = this.getViewModel().data.todos;

  24. //newToDo 就是文字框的 引用

  25. var desc = this.lookupReference('newToDo').value.trim();

  26. if (desc != '') {

  27. store.add({

  28. desc: desc

  29. });

  30. store.sync({

  31. success: function (batch, options) {

  32. this.lookupReference('newToDo').setValue('');

  33. this.addToDoToView(options.operations.create[0]);

  34. },

  35. scope: this

  36. });

  37. }

  38. },

  39. addToDoToView: function (record) {

  40. this.view.add([{

  41. xtype: 'container',

  42. layout: 'hbox',

  43. cls: 'row',

  44. items: [{

  45. xtype: 'checkbox',

  46. boxLabel: record.get('desc'),

  47. checked: record.get('done'),

  48. flex: 1

  49. },{

  50. html: '<a class="hidden" href="#"><i taskId="' + record.get('id') + '" class="fa fa-times"></i></a>',

  51. }]

  52. }]);

  53. },

  54. onDelete: function (event, target) {

  55. var store = this.getViewModel().data.todos;

  56. var targetCmp = Ext.get(target);

  57. var id = targetCmp.getAttribute('taskId');

  58. store.remove(store.getById(id));

  59. store.sync({

  60. success: function () {

  61. this.view.remove(targetCmp.up('.row').id)

  62. },

  63. scope: this

  64. });

  65. }

  66. });


 

 

最後,讓我們來建立 REST 服務。Go 語言可以安裝在 mac OS,Windows,linux 等等。這裡下載 https://golang.org.

 

Go 語言安裝完成之後,你需要設定 GOROOT 環境變數為 Go 語言的安裝目錄。linux 應該新增下列命令到 $HOME/.profile:

 

export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin

 

 

針對本專案,我將使用一個名為 Gorilla 的路由模組,安裝這個模組使用以下命令:

 

go get github.com/gorilla/mux

 

 

 

以下是 REST 服務的程式碼。

 

  • 這段程式碼不會存資料到資料庫裡,所有的資料都是在記憶體中,當你關閉程式,資料將被銷燬。

 

 

package main
import ( "fmt"
"encoding/json"
"net/http" "strconv"
"github.com/gorilla/mux"
)
type Task struct {
	Id string `json:"id"`
	Desc string `json:"desc"`
	Done bool `json:"done"`
} 
var tasks map[string] *Task
func GetToDo(rw http.ResponseWriter, req * http.Request) {
	vars := mux.Vars(req)   
	task := tasks[vars["id"]]
	js, err := json.Marshal(task)
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(rw, string(js)) 
}
 
func UpdateToDo(rw http.ResponseWriter, req * http.Request) {
	vars := mux.Vars(req)
	task:= tasks[vars["id"]]
	dec:= json.NewDecoder(req.Body)   
	err:= dec.Decode( & task)   
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}
	task.Id = vars["id"]
	retjs, err:= json.Marshal(task)
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(rw, string(retjs)) 
}
 
func DeleteToDo(rw http.ResponseWriter, req * http.Request) {
	vars := mux.Vars(req)   
	delete(tasks, vars["id"])   
	fmt.Fprint(rw, "{status : 'success'}") 
}
 
func AddToDo(rw http.ResponseWriter, req * http.Request) {
	task:= new(Task)
	dec:= json.NewDecoder(req.Body)   
	err:= dec.Decode( & task)   
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)     
		return
	}
	tasks[task.Id] = task
	retjs, err:= json.Marshal(task)
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(rw, string(retjs)) 
}
 
func GetToDos(rw http.ResponseWriter, req * http.Request) {
		v := make([]*Task, 0, len(tasks))
		for _, value := range tasks {     
			v = append(v, value)
		}
	js, err:= json.Marshal(v)   
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(rw, string(js)) 
}
 
func main() {
   var port = 9001   
   router:= mux.NewRouter()   
   tasks = make(map[string] *Task)
   router.HandleFunc("/tasks", GetToDos).Methods("GET")   
   router.HandleFunc("/tasks", AddToDo).Methods("POST")   
   router.HandleFunc("/tasks/{id}", GetToDo).Methods("GET")   
   router.HandleFunc("/tasks/{id}", UpdateToDo).Methods("PUT")   
   router.HandleFunc("/tasks/{id}", DeleteToDo).Methods("DELETE")   
   router.PathPrefix("/").Handler(http.FileServer(http.Dir("../")))
   fmt.Println("Listening on port", port)
   http.ListenAndServe("localhost:" + strconv.Itoa(port), router) 
}

 

 

 

使用下列命令執行服務:

 

go run ToDo.go

 

 

如果沒有報錯,應該顯示類似於以下程式碼:

 

Listening on port 9001


 

 

現在你可以訪問 localhost:9001 檢視應用了:

完整的程式碼在這裡 https://github.com/ananddayalan/extjs-byexample-todo.

 

總結

在本章中,你學習瞭如何建立 model,store,代理和如何處理資料。同時也看到如何使用 Go 語言建立一個簡單的 REST 服務。