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前辈,和阿源哥哥前辈都有写过类似的文章 ( 晕倒 ),但不管怎样,这是小弟自己边看边写的读书笔记啦><,所以如果小弟没写好的地方,也可以去看看前辈们写的超详细文章喔!!

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