• ASP.NET MVC 4 (十二) Web API


    Web API属于ASP.NET核心平台的一部分,它利用MVC框架的底层功能方便我们快速的开发部署WEB服务。我们可以在常规MVC应用通过添加API控制器来创建web api服务,普通MVC应用程序控制器根据用户请求的action方法返回ActionResult,而web api服务返回的则是json封装的模型数据。

    在开始下面的内容前先给出相关类与接口的代码:

    public interface IReservationRepository {
    
            IEnumerable<Reservation> GetAll();
            Reservation Get(int id);
            Reservation Add(Reservation item);
            void Remove(int id);
            bool Update(Reservation item);
        }
    
    public class Reservation {
    
            public int ReservationId { get; set; }
            public string ClientName { get; set; }
            public string Location { get; set; }    
        }
    
    public class ReservationRepository : IReservationRepository {
            private List<Reservation> data = new List<Reservation> {
                new Reservation {ReservationId = 1,  ClientName = "Adam", Location = "London"},
                new Reservation {ReservationId = 2,  ClientName = "Steve", Location = "New York"},
                new Reservation {ReservationId = 3,  ClientName = "Jacqui", Location = "Paris"},
            };
    
            private static ReservationRepository repo = new ReservationRepository();
            public static IReservationRepository getRepository() {
                return repo;
            }
    
            public IEnumerable<Reservation> GetAll() {
                return data;
            }
    
            public Reservation Get(int id) {
                var matches = data.Where(r => r.ReservationId == id);
                return matches.Count() > 0 ? matches.First() : null;
            }
    
            public Reservation Add(Reservation item) {
                item.ReservationId = data.Count + 1;
                data.Add(item);
                return item;
            }
    
            public void Remove(int id) {
                Reservation item = Get(id);
                if (item != null) {
                    data.Remove(item);
                }
            }
    
            public bool Update(Reservation item) {
                Reservation storedItem = Get(item.ReservationId);
                if (storedItem != null) {
                    storedItem.ClientName = item.ClientName;
                    storedItem.Location = item.Location;
                    return true;
                } else {
                    return false;
                }
            }
        }

    创建API控制器

    在已有的MVC WEB应用中添加API控制器来创建WEB服务,VS的添加控制器对话框中可以选择创建API控制器,我们可以选择“Empty API controller”创建不包含任何方法的空API控制器,手工添加对应各个WEB服务操作的方法,一个完整的API控制类类似:

    using System.Collections.Generic;
    using System.Web.Http;
    using WebServices.Models;
    
    namespace WebServices.Controllers {
        public class ReservationController : ApiController {
            IReservationRepository repo = ReservationRepository.getRepository();
    
            public IEnumerable<Reservation> GetAllReservations() {
                return repo.GetAll();
            }
    
            public Reservation GetReservation(int id) {
                return repo.Get(id);
            }
    
            public Reservation PostReservation(Reservation item) {
                return repo.Add(item);
            }
    
            public bool PutReservation(Reservation item) {
                return repo.Update(item);
            }
    
            public void DeleteReservation(int id) {
                repo.Remove(id);
            }
        }
    }

    当我们从浏览器访问 /api/reservation时得到的GetAllReservations方法封装的JSON数据,在IE10中得到的结果类似:

    [{"ReservationId":1,"ClientName":"Adam","Location":"London"}, 
    {"ReservationId":2,"ClientName":"Steve","Location":"New York"}, 
    {"ReservationId":3,"ClientName":"Jacqui","Location":"Paris"}] 

    如果是Chrome或者Firefox结果则是XML:

    <ArrayOfReservation> 
      <Reservation> 
        <ClientName>Adam</ClientName> 
        <Location>London</Location> 
        <ReservationId>1</ReservationId> 
      </Reservation> 
      <Reservation> 
        <ClientName>Steve</ClientName> 
        <Location>New York</Location> 
        <ReservationId>2</ReservationId> 
      </Reservation> 
      <Reservation> 
        <ClientName>Jacqui</ClientName> 
        <Location>Paris</Location> 
        <ReservationId>3</ReservationId> 
      </Reservation> 
    </ArrayOfReservation> 

    这种区别源于浏览器提交的Accept头,IE10发送的Accept头类似:

    ... 
    Accept: text/html, application/xhtml+xml, */* 
    ...

    表示IE10优先接受 text/html,接下来是application/xhtml+xml,如果前两者不可行, */*表示接受任何格式。Web API支持XML和JSON两种格式,但是优先使用JSON,所以IE10得到的*/*选择的JSON格式。Chrome发送的Accept头类似:

    ... 
    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
    ... 

    Chrome优先接受application/xml,所以WEB API使用XML格式发送数据。

    理解API控制器如何作用

    在IE10中如果访问/api/reservation/3,得到的JSON数据类似:

    {"ReservationId":3,"ClientName":"Jacqui","Location":"Paris"}

    这里得到的ReservationIdvalue=3的对象数据,这和MVC的路径映射很相似,实际上确实也是这样,API有自己的的路径映射,定义在 WebApiConfig.cs文件中:

    using System.Web.Http;
    
    namespace WebServices {
        public static class WebApiConfig {
            public static void Register(HttpConfiguration config) {
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
            }
        }
    }

    这里的路径映射非常类似于MVC控制器的路径映射,但是所用的路径映射表和配置类都来自于System.Web.Http命名空间,Microsoft在这个命名空间复制了MVC的对应的类,这样做使得我们可以脱离MVC单独使用WEB API。这里注册的API路径映射最后在global.asax中通过WebApiConfig.Register(GlobalConfiguration.Configuration)注册到全局配置文件中。

    和MVC控制器通过URL选择action方法不同,API控制器根据HTTP请求方法的不同来选择API控制器方法。API控制器方法的命名规则一般是HTTP方法作为前缀加上控制器的名称,比如GetReservation(这只是常规做法,DoGetReservation、ThisIsTheGetAction都是允许的),我们从浏览器访问/api/reservation所用的HTTP方法为GET,API控制器会查找所有包含GET的所有控制器方法,GetReservation和GetAllReservations都在考虑之类,但是具体选择哪个还参考了所带的参数,访问/api/reservation没有任何参数,因此API控制器选择了GetAllReservations,访问/api/reservation/3自然就选择了GetReservation。由此我们也知道PostReservation、PutReservation、DeleteReservation分别对应HTTP的Post、Put、Delete三种方法(WEB API的Representation State Transfer - REST)。

    PutReservation方法命名多少有点不自然,我们更习惯称呼它为UpdateReservation,和MVC一样,System.Web.Http也提供一些特性可以应用于控制器方法:

    public class ReservationController : ApiController {
            IReservationRepository repo = ReservationRepository.getRepository();
    
            public IEnumerable<Reservation> GetAllReservations() {
                return repo.GetAll();
            }
    
            public Reservation GetReservation(int id) {
                return repo.Get(id);
            }
    
            [HttpPost]
            public Reservation CreateReservation(Reservation item) {
                return repo.Add(item);
            }
    
            [HttpPut]
            public bool UpdateReservation(Reservation item) {
                return repo.Update(item);
            }
    
            public void DeleteReservation(int id) {
                repo.Remove(id);
            }
        }

    这里通过HttpPost特性指定CreateReservation对应HTTP的POST请求,HttpPut指定UpdateReservation对应HTTP的PUT请求,MVC也有类似的特性,但是注意它们虽然同名但是定义在System.Web.Http命名空间。

    使用WEB API

    如同常规WEB服务,我们可以有多种方式来调用WEB API,比如windows form程序、其他ASP.NET应用程序,这里给出如何在当前MVC应用中利用javascript脚本来使用web api。

    视图文件index.cshtml:

    @{ ViewBag.Title = "Index";}
    @section scripts {
        <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
        <script src="~/Scripts/Home/Index.js"></script>
    }
    <div id="summaryDisplay" class="display">
        <h4>Reservations</h4>
        <table>
            <thead>
                <tr>
                    <th class="selectCol"></th>
                    <th class="nameCol">Name</th>
                    <th class="locationCol">Location</th>
                </tr>
            </thead>
            <tbody id="tableBody">
                <tr><td colspan="3">The data is loading</td></tr>
            </tbody>
        </table>
        <div id="buttonContainer">
            <button id="refresh">Refresh</button>
            <button id="add">Add</button>
            <button id="edit">Edit</button>
            <button id="delete">Delete</button>
        </div>
    </div>
    
    <div id="addDisplay" class="display">
        <h4>Add New Reservation</h4>
        @{
            AjaxOptions addAjaxOpts = new AjaxOptions {
                OnSuccess = "getData",
                Url = "/api/reservation"
            };
        }
        @using (Ajax.BeginForm(addAjaxOpts)) {
            @Html.Hidden("ReservationId", 0)
            <p><label>Name:</label>@Html.Editor("ClientName")</p>
            <p><label>Location:</label>@Html.Editor("Location")</p>
            <button type="submit">Submit</button>
        }
    </div>
    
    <div id="editDisplay" class="display">
        <h4>Edit Reservation</h4>
        <form id="editForm">
            <input id="editReservationId" type="hidden" name="ReservationId"/>
            <p><label>Name:</label><input id="editClientName" name="ClientName" /></p>
            <p><label>Location:</label><input id="editLocation" name="Location" /></p>
        </form>
        <button id="submitEdit" type="submit">Save</button>
    </div>

    Index.js:

    function selectView(view) {
        $('.display').not('#' + view + "Display").hide();
        $('#' + view + "Display").show();
    }
    
    function getData() {
        $.ajax({
            type: "GET",
            url: "/api/reservation",
            success: function (data) {
                $('#tableBody').empty();
                for (var i = 0; i < data.length; i++) {
                    $('#tableBody').append('<tr><td><input id="id" name="id" type="radio"'
                        + 'value="' + data[i].ReservationId + '" /></td>'
                        + '<td>' + data[i].ClientName + '</td>'
                        + '<td>' + data[i].Location + '</td></tr>');
                }
                $('input:radio')[0].checked = "checked";
                selectView("summary");
            }
        });
    }
    
    $(document).ready(function () {
        selectView("summary");
        getData();
        $("button").click(function (e) {
            var selectedRadio = $('input:radio:checked')
            switch (e.target.id) {
                case "refresh":
                    getData();
                    break;
                case "delete":
                    $.ajax({
                        type: "DELETE",
                        url: "/api/reservation/" + selectedRadio.attr('value'),
                        success: function (data) {
                            selectedRadio.closest('tr').remove();
                        }
                    });
                    break;
                case "add":
                    selectView("add");
                    break;
                case "edit":
                    $.ajax({
                        type: "GET",
                        url: "/api/reservation/" + selectedRadio.attr('value'),
                        success: function (data) {
                            $('#editReservationId').val(data.ReservationId);
                            $('#editClientName').val(data.ClientName);
                            $('#editLocation').val(data.Location);
                            selectView("edit");
                        }
                    });
                    break;
                case "submitEdit":
                    $.ajax({
                        type: "PUT",
                        url: "/api/reservation/" + selectedRadio.attr('value'),
                        data: $('#editForm').serialize(),
                        success: function (result) {
                            if (result) {
                                var cells = selectedRadio.closest('tr').children();
                                cells[1].innerText = $('#editClientName').val();
                                cells[2].innerText = $('#editLocation').val();
                                selectView("summary");
                            }
                        }
                    });
                    break;
            }
        });
    });

    第一次访问index视图HTML界面装载完成后调用JS函数selectView("summary"),它显示ID=summaryDisplay的DIV块,隐藏其他的addDisplay、editDisplay块,然后通过调用JS函数getData(),getData使用GET方法向WEB API请求数据,返回的数据每个项目一行在表格中。summaryDisplay底部有Refresh、Add、Edit、Delete四个按钮,这些按钮的点击在“$("button").click(function (e)”处理;点击Refresh时调用getdata刷新数据;点击add时隐藏其他DIV块,显示addDisplay DIV块,这个DIV块创建一个AJAX表单,POST方法提交到API控制器的CreateReservation;EDIT按钮根据当前的选项从/api/reservation/{id} GET相应的对象后显示editDisplay DIV块,同时隐藏其他DIV块;点击editDisplay DIV块中的submitEdit按钮,JS使用PUT方法请求/api/reservation/{id}调用API控制器的UpdateReservation方法修改数据,完成后再次显示summaryDisplay DIV块,隐藏其他DIV块;点击delete按钮则是使用DELETE方法请求/api/reservation/{id}调用控制器方法DeleteReservation删除对象,完成后删除summaryDisplay DIV块中的相应行。

    以上为对《Apress Pro ASP.NET MVC 4》第四版相关内容的总结,不详之处参见原版 http://www.apress.com/9781430242369。 

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    Java最常用的工具类库
    运筹学那些事,专科学生学习运筹学之网络计划技术,No.6
    这么设计实时数据平台,OLAP再也不是个事儿
    从这 5 个场景 , 看 MPC 多方安全计算的行业应用
    前后端通信进行AES加密(Vue
    圣诞快乐: 用 GaussDB T 绘制一颗圣诞树,兼论高斯数据库语法兼容
    安全多方计算新突破:阿里首次实现“公开可验证”的安全方案
    矩阵元安全多方详细介绍
    MySQL InnoDB引擎如何保证事务特性
  • 原文地址:https://www.cnblogs.com/duanshuiliu/p/3709293.html
一二三 - 开发者的网上家园