ASP.NET MVC Web API - 利用jQuery進行CRUD

( 這篇文章主要來源為ASP.NET官網的教學,在經過小弟的改編與添加一堆雜七雜八的東西XD )

前期提要

通常漫畫前面都會有一個前期提要,畢竟前篇拖的時間也有點久了,所以這邊就稍微的劇情回顧一下吧XD,首先,我們談到ASP.NET MVC Web API,其實Web API就是透過HTTP的一個Web Service,所謂的Service,就是一個服務窗口,你可以透過這個窗口進行某些事情 ( 例如:傳簡訊的Web Service,就可以把電話號碼與內容透過HTTP呼叫此服務,然後就開始發送。 ),而前端的設備,舉凡普通的網頁,到最夯的Win 8 Metro Style ( 不是淡定紅茶喔! ),都可以利用使用Web Service,因為是用最常用的HTTP阿!!,而Web Service通常會回傳個訊息回來 ( 例如:簡訊服務可能會回傳有沒有發送成功 ),而ASP.NET MVC Web API預設回傳的資料是使用JSON格式,而ASP.NET MVC Web API又是使用Rest Style風格,也就是利用HTTP標準的Get、Put、Delete、Post四種命令,來對應CRUD…但到底要怎麼做呢!?讓我們拭目以待!

( 好長的前期提要喔= =…突然發現寫漫畫前期提要的人,真是厲害… )

新增一個空專案開始

這次我們的目標是建立一個簡單的客戶新增修改刪除查詢系統,整個過程並不會動到資料庫,我們會用Collection模擬的方式來進行,所以真的不會用到資料庫。

建立要撰寫JavaScript的View

首先,我們當然是要先新增一個ASP.NET MVC 4 Web Application專案,然後取個帥氣的專案名子( 相信我,有帥氣的名子,專案就成功一半了,但記得要取和這個專案有關的名子喔!這樣未來纔不會誤刪- - )

image

接下來,我們選擇空專案吧,自己來比較有感覺嘛XDD,接下來,View Engine就選擇Razor吧,自從ASP.NET MVC 3開始,Razor就變成小弟的最愛了。

image

接下來,我們先建一個Controller。

image

我們取名為HomeController,然後Template選擇Empty Controller,這個Controller未來只會建立一個View,這個View裡面,小弟我會去撰寫JavaScript的程式碼 ( 會用jQuery這個函式庫來簡化處理原本攏長的JavaScript。 )

image

建好的程式碼如下,也不用做甚麼變動。

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcWebApiCRUDDemo.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }

    }
}

接下來,我們要建立這個Controller的View,建立這個View很簡單,我們只要在程式碼的Public ActionResult Index()的Index這幾個字上,按下滑鼠右鍵,就會出現如下圖的視窗,然後就可以輕鬆地加上View ( 超適合小弟的懶人方法!! )

image

接下來,我們看一下View的name正不正確,然後取消勾選Use a layout or master page,那是因為目前我們也沒有用到master或是layout的機制,所以直接取消就可以了。

image

到這邊,這個View暫時先這樣就可以了,我們可以run run看;沒錯,就會如下圖,空白一片,我很想加上Hello World,但現在畢竟不是做ASP.NET MVC 4的Demo,所以暫時先這樣就可以了,晚一點,我們會回頭再加上東西。

image

開始建立資料

好,看了那麼多圖,應該比較不會想睡覺了吧XDD,接下來我們Key一下程式碼吧。

建立客戶類別

我們要先建立好我們的Model,而這次整個專案,最重要的就是客戶這個Model,所以我們要先建立這個Class,我們從Model的資料夾準備加入一個新的Class。

image

接下來,我們選擇類別,並命名為Customer。

image

然後加入Id、Name、Phone,就是這麼的簡單,如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcWebApiCRUDDemo.Models
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
    }
}


 

到這邊,我們Model就建立好了,這個Customer就像是一筆資料一樣,接下來,我們要開始準備實做倉儲的地方,也就是像存放這些Customer的Collection。

實作Repository Patten

這種做法實際上是一種Repository Patten,又稱為倉儲模式,它的用途是在資料庫間多加一層,讓我們將資料存取的方法給隔離,所以我們就不會在整個專案世界到處都塞滿一堆SQL,我們會把所有存取資料庫的方法都放到Repository Class裡面來,所以就如其名,他就像是一個倉庫,存放著所有的資料,我們需要甚麼資料,就透過他來提取 ( 當然,新增、刪除也是嚕 ),這裡小弟我這邊就不細講了,基本上這個東西和ASP.NET MVC是完全沒關係的,但我們通常寫ASP.NET MVC的時候,都會利用這種Patten,來與資料庫溝通,有興趣的可以去Google一下=v=。

首先,我們一樣在Model的目錄下加上一個介面,不要看到介面就覺得很可怕,想離開這頁XDD,就如小弟說,這個Patten,是和ASP.NET MVC無關的,就算真的不懂,也可以照樣畫葫蘆,很簡單的,而且如果人家問的話,就可以很臭屁的說,這是一個Patten!!,這樣有沒有感覺很讚了XDDD。

image

接下來,我們要在這個介面裡面定義一些方法,看到這些方法,有沒有豁然開朗的感覺了!?,沒錯,我們這邊定義的方法,其實就是CRUD嘛,就如上面所說,我們就是打算透過Repository Class的這些方法來操作資料庫的CRUD,這樣子,我們的SQL就不換散落在世界各地了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcWebApiCRUDDemo.Models
{
    //這裡要實作ICustomerRepository
    public class CustomerRepository :ICustomerRepository
    {
        //我們利用List來充當資料庫。
        private List<customer> _customers = new List<customer>();
        private int _nextId = 1;//這個用途是拿來當作index

        public CustomerRepository()
        {
            //總不能都是空吧>"<,所以一開始塞一些假資料。
            this.Add(new Customer() { Name = "Sky", Phone = "0988888888" });
            this.Add(new Customer() { Name = "Andy", Phone = "0977777777" });
            this.Add(new Customer() { Name = "Tracy", Phone = "0966666666" });
        }
        
        //我們未來可以透過這個方法取得所有的資料
        public IQueryable<customer> GetAll()
        {
            //反正就是回傳整個List,並轉成IQueryable型別
            return _customers.AsQueryable();
        }

        //我們未來可以透過這個方法取得單筆資料
        public Customer Get(int id)
        {
            //尋找符合id的資料
            return _customers.Find(c => c.Id == id);
        }

        ////我們未來可以透過這個方法新增資料
        public Customer Add(Customer customer)
        {
            //index終於有用了,新增一筆前,我們先把目前的id給Customer.Id
            //並且把_nextId++
            customer.Id = _nextId++;
            _customers.Add(customer);//加入List
            return customer;
        }
        ////我們未來可以透過這個方法刪除資料
        public bool Delete(int id)
        {   //先找到目標
            Customer customer = _customers.Find(c => c.Id == id);
            //如果找不到,就回傳False
            if (customer == null)
            {
                return false;
            }
            //然後刪除
            _customers.Remove(customer);
            return true;
        }

        ////我們未來可以透過這個方法更新資料
        public bool Update(Customer customer)
        {
            //先找找看index
            int index = _customers.FindIndex(c => c.Id == customer.Id);
            if (index == -1)
            {
                return false;
            }
            //更新資料
            _customers[index].Name = customer.Name;
            _customers[index].Phone = customer.Phone;
            return true;
        }
    }
}

到目前為止,我們就把整個資料準備好了,到這邊讓小弟我休息一下,去泡的淡定紅茶吧XDD。

後記

老實說,Repository算是一個不錯的好物,不但有上面說的優點,更可以方便測試,而且未來針對Controller的部分,要寫Unit Test就更加容易了,下一篇,我們會開始針對Web API,看看怎樣配合Repository,並且把Web API給完成!。

前篇介紹了Model的實作,現在我們準備要進入Controller了喔,也就是開始撰寫Web Service!

REST

在開始寫Controller之前,我們要稍微回憶一下REST風格,所謂的REST風格,就是同一個URI下,利用HTTP的四大命令"Get、Post、Put、Delete"來配合CRUD,而不像以前取得資料是一個網址,刪除資料又是一個網址,我們可以看看下面的表。

URI Get Post Put Delete
http://xxx/api/customer 取得整組資料 新增整組資料 更新整組資料 刪除整組資料
http://xxx/api/customer/12 取得單筆資料 新增單筆資料 更新單筆資料 刪除單筆資料

從這邊我們可以看到,URI ( 資源,其實就是一個網址 )都是同一個,但是我們針對不同的HTTP命令,就會有不同的效果,而這些效果剛好符合CRUD,而這邊在強調一下,REST並不是一種規範,而是一種風格,利用HTTP的四大命令來對應資料庫的CRUD,但實際是你也可以使用Get命令來執行DB的Delete,但這樣就不符合REST風格了。

建立Web API吧

瞭解了這些規則後,接下來,我們要做的就是,如何使用ASP.NET MVC 建立符合REST風格的Web API;首先,第一步當然是先建立Controller嚕,也就是這篇的主題。

image

接下來,先把CustomerController名稱打好,但是這次的樣板則是選擇API controller with empty read/write actions這種樣板了喔!

image

完成後,我們會發現,系統自動幫我們準備好了這些程式碼,建立好的Class裡面的Function,也剛好就是上頭提到的Get、Post、Put、和Delete!!剛好就對應到HTTP的四個命令!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;

namespace MvcWebApiCRUDDemo.Controllers
{
    public class CustomerController : ApiController
    {
        // GET /api/customerontroller
        public IEnumerable<STRING> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET /api/customerontroller/5
        public string Get(int id)
        {
            return "value";
        }

        // POST /api/customerontroller
        public void Post(string value)
        {
        }

        // PUT /api/customerontroller/5
        public void Put(int id, string value)
        {
        }

        // DELETE /api/customerontroller/5
        public void Delete(int id)
        {
        }
    }
}</STRING>

修改開始

不過這畢竟不是我們要的,如果能自動產生出我們想要的東西,就好了,但現實總是殘酷的;所以我們要重新修改一下程式碼,完成後的程式碼會長這樣,大家可以先看一下,細節我們下面會再仔細介紹。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using MvcWebApiCRUDDemo.Models;
using System.Net;

namespace MvcWebApiCRUDDemo.Controllers
{
    public class CustomerController : ApiController
    {
        //建立一個靜態的倉儲,這裡使用靜態的目的是為了讓資料可以被CRUD,
        //而不會因,重新建立此倉儲物件,而造成重塞資料的問題。
        private static readonly ICustomerRepository _repository = new CustomerRepository();

        // GET /api/customer
        public IEnumerable<CUSTOMER> Get()
        {
            return _repository.GetAll();
        }

        // GET /api/customer/5
        public Customer Get(int id)
        {
            Customer customer = _repository.Get(id);
            if (customer == null)
            {
                //如果找不到,就拋出HTTP的Response例外,內容是尋找不到,也就是404
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return customer;
        }

        // POST /api/customer
        public HttpResponseMessage Post(Customer customer)
        {
            customer = _repository.Add(customer);
            var response = new HttpResponseMessage<CUSTOMER>(customer, HttpStatusCode.Created);

            string uri = Url.Route(null, new { id = customer.Id });
            response.Headers.Location = new Uri(Request.RequestUri, uri);

            return response;
        }

        // PUT /api/customer/5
        public void Put(int id, Customer customer)
        {
            customer.Id = id;
            if (!_repository.Update(customer))
            {
                //如果找不到,就拋出HTTP的Response例外,內容是尋找不到,也就是404
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

        // DELETE /api/customer/5
        public HttpResponseMessage Delete(int id)
        {
            _repository.Delete(id);
            return new HttpResponseMessage(HttpStatusCode.NoContent);
        }
    }
}</CUSTOMER></CUSTOMER>

Repository

首先我們在這個Controller先建立一個static的Repository類別,這個類別就是我們上一章準備好的倉儲物件,也就是會提供給我們新增修改Collection方法的地方 ( 別忘了,我們這個範例使用Collection來代替資料庫 ),這邊使用靜態物件的原因,是因為,如果使用非靜態物件,每次產生這個倉儲物件時,都會自動塞資料到Collection,所以我們利用靜態物件來處理,這樣子,這個倉儲物件,就只會產生一次,淡定的在那邊=v=。

( 備註一下,並不是Repository Patten都要使用靜態物件!這裡使用靜態的原因就如上面所說,但如果今天我們是針對資料庫,我們就可以不需要使用靜態物件,另外,有人可能會疑惑,為什麼要使用介面,這其實是為了測試阿!!,當我們希望針對這個Controller進行Unit Test的時候,我們就可以Mock一個假的倉儲物件,來"模擬"新增修改刪除,所以這邊纔要使用介面,這也就是使用Repository Patten的精隨。 )

private static readonly ICustomerRepository _repository = new CustomerRepository();

當有了倉儲物件,我們就可以輕易地去做新增修改等方法,如下,這就是很典型的取得所有資料的方法。

_repository.GetAll();

就是這麼簡單。

Get

接下來,我們看看第一個Get方法,這個方法其實就會對應到HTTP的Get命令,簡單的說,當我們對/api/customer這個URI,使用HTTP的Get命令,就會執行到這個Function,然後就會取得一堆JSON的資料,這就是ASP.NET MVC Web API的特色;當然,如果熟悉ASP.NET MVC的人,可能會想說,Get這個Action ( 對MVC來說Action其實就是個Function )的網址應該是xxx/Get阿!?以前不都是這樣搞的嗎!?,但不要懷疑,的確也可以像以前一樣的網址,但這部分後續章節才會講到。回頭看Web API,Web API 裡面的Function名稱,如果取名為Get,就會對應到HTTP的Get命令,反正只要記住,在ASP.NET MVC Web API底下( 也就是繼承了ApiController的Controller ),預設的Function名稱,只要對應到HTTP四大命令的名稱,就是會去對應到HTTP的四大命令。另外,如果有人看過ASP.NET MVC官網的範例,會發現,人家的範例是寫GetAllContacts(),不用懷疑,其實Function的名稱,只要前面有符合HTTP四大命令的名稱,就會對應到了。

// GET /api/customer
public IEnumerable<CUSTOMER> Get()
{
    return _repository.GetAll();
}</CUSTOMER>

如果使用IE進行分析,就可以看到對這個網址進行了GET請求。( 底下的圖,是整個專案寫完後,執行並截圖下來的,因為View的地方還沒有講到,所以這邊就只截分析的片段,等講道View的部分,會再帶大家看一次。 )

image

會回應JSON格式,沒錯,不用懷疑,ASP.NET MVC 預設就是回應JSON的格式,基本上JSON的格式還滿簡單的,如果對JSON不瞭解的人,可以參考一下這篇的中間,或是這篇的最後面有參考連結。

image

還是Get

接下來,我們繼續往下看,接下來,還是Get,但這次的Get帶了參數,沒錯,這個Get的用途就是取得單筆的資訊,我們可以透過/api/customer/5,來取得id為5的資料回來;而如果找不到,我們就會拋出一個例外,這個例外大家應該很熟習,就是傳說中的404。

// GET /api/customer/5
public Customer Get(int id)
{
    Customer customer = _repository.Get(id);
    if (customer == null)
    {
        //如果找不到,就拋出HTTP的Response例外,內容是尋找不到,也就是404
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return customer;
}

我們會發現,還是使用Get( 廢話XDD )。

image

這裡只回應單筆了。

image

Post

然後接著的是Post,也就是準備要做新增的處理,但這裡的新增有比較不一樣的地方,首先,我們會將Customer這個Model傳進去來處理,然後當然也是用倉儲Class來進行新增的動作,但比較特別的返回型別,這裡返回的是一個HttpReponseMessage,那是因為,Web API框架預設的回應狀態為200(OK),但在HTTP/1.1的協定裏面,如果使用POST創立一個資源時,因該是要返回201( Created ),這部分未來不知道正式版會不會修正,但現階段,我們只能自己手寫的方式( 或是直接拷貝貼上的方式XDD ),來處理201的狀態,這也就是為什麼需要返回一個HttpResponseMessage型別的原因;那這樣就ok了嗎?當然不,根據協定,除了要返回201以外,還必須在回應的Headers裡面的Location加上新資源的URI,這樣纔是完整的!!反正結論就是必須加上下面這些東西,還是衷心的期望,正式版可以符合懶人小弟我的需求XDDD

// POST /api/customer
public HttpResponseMessage Post(Customer customer)
{
    customer = _repository.Add(customer);
    var response = new HttpResponseMessage<CUSTOMER>(customer, HttpStatusCode.Created);

    string uri = Url.Route(null, new { id = customer.Id });
    response.Headers.Location = new Uri(Request.RequestUri, uri);

    return response;
}</CUSTOMER>

依照慣例,我們還是要貼一下圖,這次我們使用了POST了。

image

然後我們可以看一下,送出去了那些東西 ( 好啦,我承認我懶惰,把名字和電話都打"2" )

image

接下來,我們可以看到,回應的部分,的確如預期是回應201 Created了,並且在Location裡面有附上URI,因為是第四筆,所以後面多了4。

image

接下來,我們來看看Update。

Update

Update對應的其實就是HTTP的Put,這大家應該就比較少見了,但是其實還是依樣簡單,基本上帶入兩個參數,第一個參數id,是透過網址的方式傳進來的,第二個參數Customer,未來則是會用JSON格式傳遞過來,那內容其實也很簡單,畢竟我們將更新的方法都封裝到Repository Class裡面去了,我們只要簡單的呼叫_repository.Update就可以了,如果回傳false,就跳出錯誤訊息,代表找不到。

// PUT /api/customer/5
public void Put(int id, Customer customer)
{
    customer.Id = id;
    if (!_repository.Update(customer))
    {
        //如果找不到,就拋出HTTP的Response例外,內容是尋找不到,也就是404
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}

這次使用PUT了,所以會看到,請求使用PUT命令,而且可以看到網址最後有1,表示我們更改第一筆。

image

這次稍微多打了幾個字,我們把第一筆資料的name改為MS,phone也改為MS。( 由此可知,我們的UI和後面的Server端是完全沒檢查的XDDD,未來在教大家如何使用Model進行驗證,方法和ASP.NET MVC的方法其實是一樣的喔! )

image

Delete

終於寫到最後一個了,寫到快斷氣了= =,Delete通常都是號稱最簡單的XDD,( 破壞果然比建設容易 ),但這邊比較特別的是,這裡會返回一個204,也就是NoContent,根據HTTP/1.1,如果刪除了,可以返回200,或是202 ( 表示已經接受,但還沒刪除 ),或是204 ( 代表的是完成了請求,且不需要返回任何東西 ),這裡204是最適用於Delete,詳細可以參考這裡

// DELETE /api/customer/5
public HttpResponseMessage Delete(int id)
{
    _repository.Delete(id);
    return new HttpResponseMessage(HttpStatusCode.NoContent);
}

然後,我們依樣看一下圖,這次使用了DELETE命令。

image

回應的是204 No Contant。

image

後記

這篇又打了超久,但至少未來如果自己懶得打程式,也可以直接拷貝和貼上使用XDD;我們從這邊可以感受到Repository Patten所帶來的一些好處,也深深的瞭解到ASP.NET MVC Web API的寫法,更加知道了Web Service應該回應的status,下一篇,我們將進入View的世界,使用JavaScript和jQuery來撰寫AJAX的應用程式,來呼叫這裡寫好的Web Service!!

終於進入到View篇了,本來是預計一天把三篇寫完的,結果沒想到變成一個禮拜一篇= =,現在終於知道連載的辛苦了,富木堅,對不起以前都一直罵你XDD;回到主題,上一篇我們介紹到Controller,也把整個Web Service完成了,但是我們還沒講到要怎樣利用JavaScript ( 利用jQuery這個函式庫 )對Web Service進行呼叫阿!?所以我們這篇來談談View的部分。

jQuery

小弟就不再這邊詳細的介紹jQuery了,我相信很多人應該都會使用,這邊只簡單的介紹一下jQuery是做甚麼的;jQuery 是一個非常方便,快速,程式碼又簡潔的JavaScript函式庫,原本我們用JavaScript來進行DOM物件的尋找、處理事件、動畫、瀏覽器版本還有Ajax等等,都會寫非常多的程式碼,所以就有位天才John Resig,寫了jQuery來大幅的簡化,總之,就是一個好物就是了;如果不會jQuery,可以參考一下jQuery的官網或是暗黑前輩的超完整教學,所以這部分小弟就不多介紹嚕。

Knockout

這裡必須還要再提一個東西,就是Knockout,他也是一個JavaScript的程式庫,不過不用擔心,小弟沒那麼慘忍XDD,一下要K Repository Patten,一下要看ASP.NET MVC Web API,一下又要看jQuery,所以這篇文章小弟不會用到Knockout;但為什麼要提到這個呢!?那是因為目前這個東西也正是被納入到ASP.NET MVC裡面,而且官方的範例中,就是大量地使用到這個程式庫,Knockout主要的用處是利用MVVM模式,來繫節畫面上的UI;詳細可以參考暗黑前輩的Knockout這篇文章,或是官網,或是Andy前輩的FAQ Book;不過在一次的重申,這篇文章不會用到Knockout,所以可以放鬆心情的去讀這幾位前輩的文章=v=。

View

前面鋪完路後,我們終於要正式開始撰寫View了,不知道大家還記不記得,第一章的時候,那個空白的圖?那時候我們建立起View後,並沒有在View裡面添加甚麼,現在我們終於要開始加上一些東西了;首先,我們可以先打開如下圖的檔案,這就是我們第一章就準備好的檔案。

image

我們首先先準備一下畫面,我們預期的畫面如下圖,基本上和官網的差不多 ( 官網範例沒有Delete喔XDD )。

image

HTML

接下來,HTML要怎樣寫哩,其實也沒甚麼特別的,就是利用了一些HTML5的標籤 ( 畢竟已經是HTML5的時代了 ),然後準備好一個Table,來顯示資料,並且準備一些輸入欄位,以便後續的CRUD( 這裡範例是用DIV標籤來包輸入欄位,當然也可以用DD、DT、或是li等標籤,看個人喜好吧 ),當然,我們也要準備一些 Button來觸發事件,所以我們準備了很多的Button ( 不是Submit按鈕喔!!兩個是有差異的。 ),來觸發各種事件,完成大致上如下。( 眼力好的人,可能已經會發現Button裡面已經有寫準備觸發的事件的Function名稱了,我們等下就會把這些Function建立起來了喔! )

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>

<body>
    <div id="body">
        <section class="content-wrapper main-content">
            <h3>Contacts</h3>
            <table>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Name</th>
                        <th>Phone</th>
                    </tr>
                </thead>
                <tbody id="customers">
                </tbody>
            </table>
        </section>
        <section id="detail" class="content-wrapper">
            <h3>View Contact</h3>
            <label for="contactId">ID</label>
            <input type="text" title="ID" id="contactId" size="5" />
            <input type="button" value="Get" οnclick="find();" />
            <div>
                <label for="name">Name</label>
                <input type="text" title="Name" id="name" />
            </div>
            <div>
                <label for="phone"> Phone</label>
                <input type="text" title="Phone" id="phone" />
            </div>
            <div>
                <input type="button" value="Update" οnclick="update();" />
                <input type="button" value="Delete" οnclick="del();" />
                <input type="button" value="Add New" οnclick="add();" />
            </div>
        <div>
            <p id="status"></p>
        </div>
        </section> 
    </div>
</body>
</html>

接下來,我們稍微弄一下美化吧,所以我們利用CSS進行美化,( 再次強調,不要用HTML做為美化的工具,美化的職責應該是由CSS負責的喔!! )。

CSS

好,不用擔心,我們沒有用到CSS3,下面是個很簡單的CSS,把CSS放到head標籤底下;CSS的內容也超簡單,基本上也只是把table、tr、th、td上個顏色,畢竟小弟現在不是要寫CSS的筆記,所以稍微設定一下而已;另外,小弟特別把從頭擷取index.cshtml的程式碼,我想這樣大家會比較瞭解CSS要放到哪邊。

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

<style type="text/css">
    table
    {
        border: 1px solid #000;
        border-collapse: collapse;
        color: #666666;
        min-width: 200px;
    }
    
    tr
    {
        border: 1px solid #000;
        line-height: 25px;
    }
    
    th
    {
        background-color: #B1C3CC;
        color: #000;
        font-size: 13px;
        text-align: left;
    }
    
    th, td
    {
        padding-left: 5px;
    }
</style>

</head>

完成後,就會變成這樣,有沒有有沒有,變漂亮了吧!!( 好吧,其實也沒漂亮到哪去…但我們這篇的重點是jQuery和Web Service,小細節就不要計較了XDD)。

image

JavaScript & jQuery

好,完成畫面後,就可以隨便亂點了,然後就會在偵錯工具出現這些錯誤( 記得偵錯工具要打開…),這當然很正常,因為我們JavaScript還沒開始寫嘛。

image

接下來我們開始寫JavaScript,以下是整個JavaScript,我們可以把整個JavaScript放到head標籤裡面,我們後面會針對細節做介紹。 ( 好的,我知道一些JavaScript放在head,效能等等之類的問題,但是,這不是我們這篇的重點=v= )

<script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"></script>
<script type="text/javascript">

    //清空status區段
    function clearStatus() {
        $('#status').html('');
    }

    var API_URL = "api/Customer/";

    //增加資料
    function add() {

        clearStatus();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL,
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: json,
            statusCode: {
                201 /*Created*/: function (data) {
                    getAll();
                }
            }
        });
    }

    //尋找資料
    function find() {

        clearStatus();

        var id = $('#contactId').val();
        if (id != "") {
            $.getJSON(API_URL + id,
            function (data) {
                $("#customers tr").remove();
                $("#customers").append("<tr>" +
                          "<td>" + data["Id"] + "</td>" +
                          "<td>" + data["Name"] + "</td>" +
                          "<td>" + data["Phone"] + "</td>" +
                          "</tr>");
            })
            .fail(
            function (xhr, textStatus, err) {
                $('#status').html('Error: ' + err);
            });
        } else {
            getAll();
        }
    }

    //更新資料
    function update() {
        clearStatus();

        var id = $('#contactId').val();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'PUT',
            contentType: 'application/json; charset=utf-8',
            data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //刪除指定資料
    function del() {
        clearStatus();

        var id = $('#contactId').val();

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'DELETE',
            contentType: 'application/json; charset=utf-8',
            //data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //取得所有資料
    function getAll() {
        clearStatus();

        //利用Get方式取得。
        $.getJSON(API_URL,
        function (data) {
            $("#customers tr").remove();
            for (i = 0; i < data.length; i++) {
                $("#customers").append("<tr>" +
                              "<td>" + data[i].Id + "</td>" +
                              "<td>" + data[i].Name + "</td>" +
                              "<td>" + data[i].Phone + "</td>" +
                              "</tr>");
            }
        })
    .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }

    //開始時,先把資料讀取進來
    $(document).ready(function () {
        getAll();
    });


</script>

接下來,我們針對每個細部,做一個講解,有講不好的地方,不要鞭我喔><。

清空

我們的第一個function,主要用途是清除status的資訊,在HTML裡面,有一段會顯示錯誤訊息等資訊,所以我們這邊也要準備一下,清除這些資訊的Function。

//清空status區段
    function clearStatus() {
        $('#status').html('');
    }

接下來繼續。

取得全部

我們先來介紹取得全部的這個Funciton,也就是getAll(),我們這邊利用jQuery的getJSON API進行處理,這個API預設會用HTTP的GET命令;當順利取得資料的時候,就會把HTML table裡面的東西移除掉,然後再利用迴圈的方式,把取得的JSON資料和HTML標籤td,一起塞到Table裡面去。

//取得所有資料
function getAll() {
    clearStatus();

    //利用Get方式取得。
    $.getJSON(API_URL,
    function (data) {
        $("#customers tr").remove();
        for (i = 0; i < data.length; i++) {
            $("#customers").append("<tr>" +
                          "<td>" + data[i].Id + "</td>" +
                          "<td>" + data[i].Name + "</td>" +
                          "<td>" + data[i].Phone + "</td>" +
                          "</tr>");
        }
    })
.fail(
    function (xhr, textStatus, err) {
        $('#status').html('Error: ' + err);
    });
}

然後我們來看看新增。

ADD

第二段我們要講的是ADD,但我們談論ADD之前,我們要先設一個變數,負責記錄網址位置,也就是API_URL;接下來,因為是新增,所以我們會需要準備傳遞資料,所以我們利用JSON.stringify來將我們填入表單的資料,轉成JSON格式。然後我們就要利用jQuery的ajax API來對Web Service進行呼叫;還記得嗎?HTTP的POST就是新增的意思,所以我們Type會設定POST,並且等傳回201時,執行getAll()這個Function。

var API_URL = "api/Customer/";

    //增加資料
    function add() {

        clearStatus();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL,
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: json,
            statusCode: {
                201 /*Created*/: function (data) {
                    getAll();
                }
            }
        });
    }

這樣ADD就完成了,下圖是執行結果,我們填入AA、AA的資料 ( Phone欄位沒有驗證,我真的知道><,是我的錯。 ),按下AddNew後,畫面會自動更新Table,出現第四筆資料;而下面的分析工具可以看到,真的送出了POST。

image

Find

接下來是尋找資料,其實尋找資料和getAll()很像,就不多加敘述了,但比較特別的是,我們會在網址 (API_URL)後面加上id,來尋找到想要找到的那一筆;如果沒有找到,就會在status區塊報錯。

//尋找資料
function find() {

    clearStatus();

    var id = $('#contactId').val();
    if (id != "") {
        $.getJSON(API_URL + id,
        function (data) {
            $("#customers tr").remove();
            $("#customers").append("<tr>" +
                      "<td>" + data["Id"] + "</td>" +
                      "<td>" + data["Name"] + "</td>" +
                      "<td>" + data["Phone"] + "</td>" +
                      "</tr>");
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    } else {
        getAll();
    }
}

執行結果如下,table會更新成尋到到的那筆,在下面的偵錯視窗可以看到,利用了Get。

image

Update

更新資料和新增資料很像,一樣是利用JSON.stringify來轉換成JSON格式,然後用PUT來進行傳送。

//更新資料
function update() {
    clearStatus();

    var id = $('#contactId').val();

    var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

    $.ajax({
        url: API_URL + id,
        cache: false,
        type: 'PUT',
        contentType: 'application/json; charset=utf-8',
        data: json,
        success: function () { getAll(); }
    })
    .fail(
    function (xhr, textStatus, err) {
        $('#status').html('Error: ' + err);
    });
}

執行結果如下,我們ID、Name、Phone都填好後,按下Update就會更新資料,並且取得最新的table,我們也可以看到下面的偵錯工具,可以發現到現在是用HTTP PUT命令。

image

Delete

刪除資料更簡單了,因為連JSON都不需要了,我們只要URL配合id,並且送出DELETE的指令就可以了!

//刪除指定資料
function del() {
    clearStatus();

    var id = $('#contactId').val();

    $.ajax({
        url: API_URL + id,
        cache: false,
        type: 'DELETE',
        contentType: 'application/json; charset=utf-8',
        //data: json,
        success: function () { getAll(); }
    })
    .fail(
    function (xhr, textStatus, err) {
        $('#status').html('Error: ' + err);
    });
}

測試一下,我們填入要刪除的ID,並按下Delete按鈕,table就會自動更新,我們也可以從偵錯視窗看到目前使用的是HTTP裡面的Delete命令。

image

畫面載入完成時

這是最後一小段的程式碼,小弟我希望畫面載入完成後,table會再動載入資料進來,所以加了這段。

//開始時,先把資料讀取進來
    $(document).ready(function () {
        getAll();
    });

到這邊就大功告成,我們最後再把整個index.cshtml看一遍吧。

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

<style type="text/css">
    table
    {
        border: 1px solid #000;
        border-collapse: collapse;
        color: #666666;
        min-width: 200px;
    }
    
    tr
    {
        border: 1px solid #000;
        line-height: 25px;
    }
    
    th
    {
        background-color: #B1C3CC;
        color: #000;
        font-size: 13px;
        text-align: left;
    }
    
    th, td
    {
        padding-left: 5px;
    }
</style>
<script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"></script>
<script type="text/javascript">

    //清空status區段
    function clearStatus() {
        $('#status').html('');
    }

    var API_URL = "api/Customer/";

    //增加資料
    function add() {

        clearStatus();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL,
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: json,
            statusCode: {
                201 /*Created*/: function (data) {
                    getAll();
                }
            }
        });
    }

    //尋找資料
    function find() {

        clearStatus();

        var id = $('#contactId').val();
        if (id != "") {
            $.getJSON(API_URL + id,
            function (data) {
                $("#customers tr").remove();
                $("#customers").append("<tr>" +
                          "<td>" + data["Id"] + "</td>" +
                          "<td>" + data["Name"] + "</td>" +
                          "<td>" + data["Phone"] + "</td>" +
                          "</tr>");
            })
            .fail(
            function (xhr, textStatus, err) {
                $('#status').html('Error: ' + err);
            });
        } else {
            getAll();
        }
    }

    //更新資料
    function update() {
        clearStatus();

        var id = $('#contactId').val();

        var json = JSON.stringify({ name: $("#name").val(), phone: $("#phone").val() });

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'PUT',
            contentType: 'application/json; charset=utf-8',
            data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //刪除指定資料
    function del() {
        clearStatus();

        var id = $('#contactId').val();

        $.ajax({
            url: API_URL + id,
            cache: false,
            type: 'DELETE',
            contentType: 'application/json; charset=utf-8',
            //data: json,
            success: function () { getAll(); }
        })
        .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }
    
    //取得所有資料
    function getAll() {
        clearStatus();

        //利用Get方式取得。
        $.getJSON(API_URL,
        function (data) {
            $("#customers tr").remove();
            for (i = 0; i < data.length; i++) {
                $("#customers").append("<tr>" +
                              "<td>" + data[i].Id + "</td>" +
                              "<td>" + data[i].Name + "</td>" +
                              "<td>" + data[i].Phone + "</td>" +
                              "</tr>");
            }
        })
    .fail(
        function (xhr, textStatus, err) {
            $('#status').html('Error: ' + err);
        });
    }

    //開始時,先把資料讀取進來
    $(document).ready(function () {
        getAll();
    });


</script>
</head>
<body>
    <div id="body">
        <section class="content-wrapper main-content">
            <h3>Contacts</h3>
            <table>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Name</th>
                        <th>Phone</th>
                    </tr>
                </thead>
                <tbody id="customers">
                </tbody>
            </table>
        </section>
        <section id="detail" class="content-wrapper">
            <h3>View Contact</h3>
            <label for="contactId">ID</label>
            <input type="text" title="ID" id="contactId" size="5" />
            <input type="button" value="Get" οnclick="find();" />
            <div>
                <label for="name">Name</label>
                <input type="text" title="Name" id="name" />
            </div>
            <div>
                <label for="phone"> Phone</label>
                <input type="text" title="Phone" id="phone" />
            </div>
            <div>
                <input type="button" value="Update" οnclick="update();" />
                <input type="button" value="Delete" οnclick="del();" />
                <input type="button" value="Add New" οnclick="add();" />
            </div>
        <div>
            <p id="status"></p>
        </div>
        </section> 
    </div>
</body>
</html>

以上,終於寫完!

後記

寫完的當下,才發現KingKong前輩,和阿源哥哥前輩都有寫過類似的文章 ( 暈倒 ),但不管怎樣,這是小弟自己邊看邊寫的讀書筆記啦><,所以如果小弟沒寫好的地方,也可以去看看前輩們寫的超詳細文章喔!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章