ASP.NET WEBAPI制作TODO小應(yīng)用帽氓,前端由VUE.JS、axios構(gòu)建

剛好在一個項目里面需要WebAPI 和VUE俩块,于是我就將大抵的思路屢一下黎休,做了一個TODO小例程出來。后端使用ASP.NET WebAPI----即可以對接各種客戶端(瀏覽器玉凯,移動設(shè)備)势腮,構(gòu)建http服務(wù)的框架。WebAPI利用Http協(xié)議的各個方面來表達服務(wù)(例如 URI/request response header/caching/versioning/content format)漫仆,因此就省掉很多配置捎拯。也就是可以通過PC端、移動端甚至是移動端的APP來訪問WEBAPI盲厌,通過API對數(shù)據(jù)庫進行操作署照。

最終完成效果如下圖:通過Add Todo按鈕可以增加任務(wù)祸泪,任務(wù)完成了可以點擊完成,任務(wù)欄的字體將會出現(xiàn)刪除線以表示完成建芙,但也可以通過點擊“撤銷”來撤銷完成没隘,當(dāng)然也可以刪除任務(wù)。上面還有一個數(shù)字的禁荸,是總計未完成事件右蒲。那么我們將來完成這個簡單的應(yīng)用。

首先赶熟,我們來完成后端Web API瑰妄。

Web API功能簡介

  1. 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, update, delete)操作,通過不同的http動作表達不同的含義钧大,這樣就不需要暴露多個API來支持這些基本操作翰撑。
  2. 請求的回復(fù)通過Http Status Code表達不同含義,并且客戶端可以通過Accept header來與服務(wù)器協(xié)商格式啊央,例如你希望服務(wù)器返回JSON格式還是XML格式眶诈。
  3. 請求的回復(fù)格式支持 JSON,XML瓜饥,并且可以擴展添加其他格式逝撬。
  4. 原生支持OData。
  5. 支持Self-host或者IIS host乓土。
  6. 支持大多數(shù)MVC功能宪潮,例如Routing/Controller/Action Result/Filter/Model Builder/IOC Container/Dependency Injection。

REST風(fēng)格服務(wù)簡介

REST表示表述性狀態(tài)轉(zhuǎn)移趣苏,它代表的是運行在HTTP上的一個簡單的無狀態(tài)的架構(gòu)狡相,每一個唯一URL代表一個資源。在創(chuàng)建RESTful服務(wù)時食磕,應(yīng)遵循四個基本的設(shè)計原則:

  1. 使用HTTP方法(動詞)尽棕,使用統(tǒng)一的方式來獲取資源(交互的統(tǒng)一接口),即檢索資源使用GET彬伦,創(chuàng)建資源使用POST滔悉, 更新資源使用PUT / PATCH,刪除資源使用DELETE单绑。
  2. 與資源的交互是無狀態(tài)的回官, 因此由客戶端發(fā)起的每個請求應(yīng)當(dāng)包括HTTP請求的所有參數(shù),上下文信息和所需服務(wù)器返回數(shù)據(jù)數(shù)據(jù)類型等搂橙。
  3. 資源標(biāo)識應(yīng)通過URI來定義歉提,簡單來說應(yīng)該是只使用URI來完成服務(wù)器與客戶端和資源之間的交互。這些URI可以看作一個RESTful服務(wù)提供的接口。
  4. 支持JSON或/和XML等多種格式作為數(shù)據(jù)傳輸格式唯袄。

我的開發(fā)工具搭配比較詭異弯屈,使用E5服務(wù)器CPU、WIN10 64操作系統(tǒng)恋拷,IDE用VS2015卻配著SQL2008R2,為什么SQL版本這么低厅缺,不知道蔬顾,問我的客戶去。

廢話不說了湘捎,打開VS2015诀豁,新建一個項目再說--我把項目命名為WebAPI2Todo:

mark

注意項目選擇模板為Web API,核心引用將MVC和Web API全部點上。

在這個項目中使用NuGet軟件包管理器安裝Entity Framework 6窥妇、Jquery舷胜、Bootstrap、vue活翩,右鍵單擊“Model”文件夾烹骨,新建一個數(shù)據(jù)類TodoList.cs:

 public class TodoList
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public bool Task { get; set; }
    }

右鍵單擊Controllers文件夾,新建一個包含視圖的控制器:

mark

模型類選擇我們剛剛生成的TodoList材泄,如

mark
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using WebAPI2Todo.Models;

namespace WebAPI2Todo.Controllers
{
    public class TodoListsController : Controller
    {
        private WebAPI2TodoContext db = new WebAPI2TodoContext();

        // GET: TodoLists
        public ActionResult Index()
        {
            return View(db.TodoLists.ToList());
        }

        // GET: TodoLists/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            TodoList todoList = db.TodoLists.Find(id);
            if (todoList == null)
            {
                return HttpNotFound();
            }
            return View(todoList);
        }

        // GET: TodoLists/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: TodoLists/Create
        // 為了防止“過多發(fā)布”攻擊沮焕,請啟用要綁定到的特定屬性,有關(guān) 
        // 詳細信息拉宗,請參閱 http://go.microsoft.com/fwlink/?LinkId=317598峦树。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "ID,Name,Task")] TodoList todoList)
        {
            if (ModelState.IsValid)
            {
                db.TodoLists.Add(todoList);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(todoList);
        }

        // GET: TodoLists/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            TodoList todoList = db.TodoLists.Find(id);
            if (todoList == null)
            {
                return HttpNotFound();
            }
            return View(todoList);
        }

        // POST: TodoLists/Edit/5
        // 為了防止“過多發(fā)布”攻擊,請啟用要綁定到的特定屬性旦事,有關(guān) 
        // 詳細信息魁巩,請參閱 http://go.microsoft.com/fwlink/?LinkId=317598。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "ID,Name,Task")] TodoList todoList)
        {
            if (ModelState.IsValid)
            {
                db.Entry(todoList).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(todoList);
        }

        // GET: TodoLists/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            TodoList todoList = db.TodoLists.Find(id);
            if (todoList == null)
            {
                return HttpNotFound();
            }
            return View(todoList);
        }

        // POST: TodoLists/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            TodoList todoList = db.TodoLists.Find(id);
            db.TodoLists.Remove(todoList);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

這樣就默認生成了一個帶有CRUD的視圖姐浮,非彻人欤快捷,但是這個時候你若是去運行Create单料,在提交到數(shù)據(jù)庫的時候肯定會出錯埋凯,并且有提示 SQL Network Interfaces, error: 52 - 無法定位 Local Database Runtime 安裝 類似的提示,我就奇怪了扫尖,剛剛我已經(jīng)定位了一個數(shù)據(jù)服務(wù)器了白对,為什么還會提示本地錯誤。

打開Web.config一看换怖,原來在生成控制器的時候它就默認生成了一個本地的數(shù)據(jù)庫連接

 <add name="WebAPI2TodoContext" connectionString="Data Source=(localdb)\MSSQLLocalDB; Initial Catalog=WebAPI2TodoContext-20170617024457; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|WebAPI2TodoContext-20170617024457.mdf"
      providerName="System.Data.SqlClient" />

把它修改一下即可:

   <add name="WebAPI2TodoContext" connectionString="Data Source=.;Initial Catalog=WebAPI2Todo;User ID=sa;Password=WINdows2008"
      providerName="System.Data.SqlClient" />

你可以在MSSQL中新建一個空數(shù)據(jù)庫甩恼,取名為WebAPI2Todo,然后打開VS2015的程序包管理器控制臺,依次輸入以下命令:

1.Enable-Migrations -ContextTypeName WebAPI2Todo.Models.WebAPI2TodoContext
2.add-migration Initial
3.update-database

現(xiàn)在運行Create吧条摸,相信我不會讓你失望的悦污。至此,一個帶有視圖的CRUD 操作的項目已經(jīng)基本完成钉蒲,但我們是想要用VUE通過API進行數(shù)據(jù)操作切端,通過VUE的雙向綁定數(shù)據(jù)功能進行開發(fā)。那么就要繼續(xù)向下看了顷啼。

在根目錄下創(chuàng)建新文件夾 Interface ,然后再新增一個接口 ITodoListRepository.cs ,代碼如下:

 interface ITodoListRepository
    {
        IEnumerable<TodoList> GetAll();
        TodoList Get(int id);
        TodoList Add(TodoList item);
        bool Update(TodoList item);
        bool Delete(int id);
    }

在根目錄下新建文件夾 Repositories 踏枣,新建類TodoListRepository.cs ,以此來實現(xiàn)使用 Entity Framework 進行數(shù)據(jù)庫的CRUD 的操作方法钙蒙。

    public class TodoListRepository: ITodoListRepository
    {
        private WebAPI2TodoContext db = new WebAPI2TodoContext();

        public IEnumerable<TodoList> GetAll()
        {
             return db.TodoLists.ToList();
        }

        public TodoList Get(int id)
        {
            
            return db.TodoLists.Find(id);
        }

        public TodoList Add(TodoList item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            
            db.TodoLists.Add(item);
            db.SaveChanges();
            return item;
        }

        public bool Update(TodoList item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            var todo = db.TodoLists.Single(t => t.ID == item.ID);
            todo.Name = item.Name;
            todo.Task = item.Task;
            db.SaveChanges();
            return true;
        }

        public bool Delete(int id)
        {
            // TO DO : Code to remove the records from database
            TodoList ts = db.TodoLists.Find(id);
            db.TodoLists.Remove(ts);
            db.SaveChanges();
            return true;
        }
    }

右鍵單擊 Controllers 文件夾并添加新控制器茵瀑,模板選擇Web API 2 控制器-空,取名為'TodoController.cs':

    public class TodoController : ApiController
    {
        static readonly ITodoListRepository repository = new TodoListRepository();
        public IEnumerable GetAllTodo()
        {
            return repository.GetAll();
        }

        public TodoList PostTodo(TodoList item)
        {
            return repository.Add(item);
        }

        public IEnumerable PutTodo(int id, TodoList todo)
        {
            todo.ID = id;
            if (repository.Update(todo))
            {
                return repository.GetAll();

            }
            else
            {
                return null;
            }
        }

        public bool DeleteTodo(int id)
        {
            if (repository.Delete(id))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

基本上躬厌,現(xiàn)在后端已經(jīng)完成了差不多了马昨,下面我們進行前端開發(fā)。

在Home控制器中增加一個Test扛施,右擊ActionResult Test()添加視圖鸿捧。(注意在默認模板中加載vue.js:打開Views--Shared--_Layout.cshtml:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/css" />
    <script src="~/Scripts/modernizr-2.6.2.js"></script>
    <script src="~/Scripts/vue.js"></script>
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                </ul>
            </div>
        </div>
    </div>

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>

    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>

Test視圖加入vue.js代碼,我們一個基于本地TODO列表已經(jīng)完成:

<style>
    .Task {
        text-decoration: line-through;
    }
</style>
<nav class="navbar"></nav>
<div class="container" id="app">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">My Tasks</div>
                <div class="panel-body">
                    <h1>My Todos({{ remaining }})</h1>
                    <ul class="list-group">
                        <li class="list-group-item" :class="{'Task':todo.Task}"  v-for="(todo,index) in todos">
                            {{todo.Name}}
                            <button class="btn btn-success btn-xs pull-right" v-on:click="toggleTodo(index)" v-if="todo.Task">撤銷</button>
                            <button class="btn btn-primary btn-xs pull-right" v-on:click="toggleTodo(index)" v-else>完成</button>
                            <span class="pull-right">&nbsp;</span> 
                            <button class="btn btn-warning btn-xs pull-right" v-on:click="deleteTodo(index)">刪除</button>
</li>
                    </ul>
                    <form v-on:submit.prevent="addTodo(newTodo)">
                        <div class="form-group">
                            <input type="text" v-model="newTodo.Name" class="form-control" placeholder="輸入新事件" />
                        </div>
                        <div class="form-group">
                            <button class="btn btn-success" type="submit">Add Todo</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            todos: [
                { id: 1, Name: 'Learn Vue.js', Task: true },
                { id: 2, Name: '吃宵夜', Task: false }
            ],
            newTodo: { id: null, Name: '',Task:false }
        },
        computed: {
            remaining: function () {
                return this.todos.filter(function (todo) {
                    return !todo.Task;
                }).length;
            }
        },
        methods: {
            addTodo(newTodo) {
                this.todos.push(newTodo);
                this.newTodo = { id: null, Name: '', Task: false }
            },
            deleteTodo(index) {
                this.todos.splice(index,1)
            },
            toggleTodo(index) {
                this.todos[index].Task = !this.todos[index].Task;
            }
            
        }
    })
</script>

效果如圖:

mark

可以看出煮嫌,Vue.js用了非常少的代碼量寫出了一個本地的TODO應(yīng)用笛谦,但這完全是本地的,你做任何的增加昌阿、刪除都改變不了什么饥脑,刷新一下又從初始值開始。

那么懦冰,首先灶轰,加入axios:

  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

然后在new Vue 上面加入Vue.prototype.$http = axios;

全部代碼如下:

<script>
    Vue.prototype.$http = axios;
    new Vue({
        el: "#app",
        data: {
            num:0,//計算未完成總數(shù)
            todos: '',//空的數(shù)據(jù)
            newTodo: { id: null, Name: '', Task: false }//新建默認數(shù)據(jù)
        },
        mounted() {
            this.getData('/api/todo/GetAllTodo');//通過this.getData的URL取得API數(shù)據(jù)
          
        },
        computed: {
            remaining: function () {
                return this.num;
             }
        },//這里我們聲明了一個計算屬性 remaining,通過返回this.num的值做為未完成總數(shù)
        methods: {//VUE事件處理器
             getData(url){
                this.$http.get(url).then((response) => {
                    this.todos = response.data;
                    this.num = this.todos.filter(function (todo) {
                        return todo.Task == false;
                    }).length;
                });
             },//this.getData取得數(shù)據(jù)庫數(shù)據(jù),并取得未完成事件總數(shù)
            addTodo(newTodo) {
                this.todos.push(newTodo);
                this.num++;
                this.$http.post('/api/todo/PostTodo', newTodo).then(response=>console.log(response));
                this.newTodo = { id: null, Name: '', Task: false }
            },//添加任務(wù)刷钢,并且將數(shù)據(jù)post到URL
            deleteTodo(index,id) {
                if (!this.todos[index].Task) {
                    this.num--;
                }
                this.$http.delete('/api/todo/DeleteTodo/' +id).then(response=>console.log(response));
                this.todos.splice(index, 1);
            },//刪除任務(wù)笋颤,如果刪除的任務(wù)是未完成的,那么將對未完成總數(shù)-1
            toggleTodo(index,id) {
                var thistodo = !this.todos[index].Task;
                var thisdata={
                    Name:this.todos[index].Name,
                    Task:thistodo
                };
                this.$http.put('/api/todo/puttodo/'+id,thisdata
                    ).then(response=>console.log(response));
                thistodo ? this.num-- : this.num++;
                this.todos[index].Task = thistodo;
                           }
        },//完成任務(wù)或者撤銷任務(wù)
        
    });
    </script>

加入了WEBAPI連接代碼内地,代碼量從30行增加到50行伴澄,50行的JavaScript可以完成這一系列的功能已經(jīng)非常了不起了。

至此阱缓,一個簡單的web單頁應(yīng)用已經(jīng)完成非凌,后端使用ASP.NET、數(shù)據(jù)庫使用MSSQL荆针,前端使用Bootstrap敞嗡、Vue.js颁糟、axios,熟練的話喉悴,一個小時可以完成這個項目棱貌。

其實,我們所有的努力都希望代碼寫得少一點箕肃,效率提高多一點婚脱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市勺像,隨后出現(xiàn)的幾起案子起惕,更是在濱河造成了極大的恐慌,老刑警劉巖咏删,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異问词,居然都是意外死亡督函,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門激挪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辰狡,“玉大人,你說我怎么就攤上這事垄分⊥鹌” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵薄湿,是天一觀的道長叫倍。 經(jīng)常有香客問我,道長豺瘤,這世上最難降的妖魔是什么吆倦? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮坐求,結(jié)果婚禮上蚕泽,老公的妹妹穿的比我還像新娘。我一直安慰自己桥嗤,他們只是感情好须妻,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泛领,像睡著了一般荒吏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上师逸,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天司倚,我揣著相機與錄音豆混,去河邊找鬼。 笑死动知,一個胖子當(dāng)著我的面吹牛皿伺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盒粮,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鸵鸥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了丹皱?” 一聲冷哼從身側(cè)響起妒穴,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎摊崭,沒想到半個月后讼油,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡呢簸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年矮台,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片根时。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘦赫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛤迎,到底是詐尸還是另有隱情确虱,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布替裆,位于F島的核電站校辩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扎唾。R本人自食惡果不足惜召川,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胸遇。 院中可真熱鬧荧呐,春花似錦、人聲如沸纸镊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逗威。三九已至峰搪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凯旭,已是汗流浹背概耻。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工使套, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鞠柄。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓侦高,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厌杜。 傳聞我的和親對象是個殘疾皇子奉呛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容