• Web API 强势入门指南


    Web API是一个比较宽泛的概念。这里我们提到Web API特指ASP.NET Web API。

    这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例展示如何通过Web API构建http服务,同时也展示了Visual Studio构建.net项目的各种强大。

    目录

    什么是 Web API

    官方定义如下,强调两个关键点,即可以对接各种客户端(浏览器,移动设备),构建http服务的框架。

    ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

    Web API在ASP.NET完整框架中地位如下图,与SignalR一起同为构建Service的框架。Web API负责构建http常规服务,而SingalR主要负责的是构建实时服务,例如股票,聊天室,在线游戏等实时性要求比较高的服务。

    Picture20

     

    为什么要用 Web API

    Web API最重要的是可以构建面向各种客户端的服务。另外与WCF REST Service不同在于,Web API利用Http协议的各个方面来表达服务(例如 URI/request response header/caching/versioning/content format),因此就省掉很多配置。

    Picture2

     

    当你遇到以下这些情况的时候,就可以考虑使用Web API了。

    • 需要Web Service但是不需要SOAP
    • 需要在已有的WCF服务基础上建立non-soap-based http服务
    • 只想发布一些简单的Http服务,不想使用相对复杂的WCF配置
    • 发布的服务可能会被带宽受限的设备访问
    • 希望使用开源框架,关键时候可以自己调试或者自定义一下框架

    功能简介

    Web API的主要功能

    1. 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, update, delete)操作

        通过不同的http动作表达不同的含义,这样就不需要暴露多个API来支持这些基本操作。

    2. 请求的回复通过Http Status Code表达不同含义,并且客户端可以通过Accept header来与服务器协商格式,例如你希望服务器返回JSON格式还是XML格式。

    3. 请求的回复格式支持 JSON,XML,并且可以扩展添加其他格式。

    4. 原生支持OData

    5. 支持Self-host或者IIS host。

    6. 支持大多数MVC功能,例如Routing/Controller/Action Result/Filter/Model Builder/IOC Container/Dependency Injection。

    Web API vs MVC

    你可能会觉得Web API 与MVC很类似,他们有哪些不同之处呢?先上图,这就是他们最大的不同之处。

    Picture1

    详细点说他们的区别,

    • MVC主要用来构建网站,既关心数据也关心页面展示,而Web API只关注数据
    • Web API支持格式协商,客户端可以通过Accept header通知服务器期望的格式
    • Web API支持Self Host,MVC目前不支持
    • Web API通过不同的http verb表达不同的动作(CRUD),MVC则通过Action名字表达动作
    • Web API内建于ASP.NET System.Web.Http命名空间下,MVC位于System.Web.Mvc命名空间下,因此model binding/filter/routing等功能有所不同
    • 最后,Web API非常适合构建移动客户端服务

    Web API vs WCF

    发布服务在Web API和WCF之间该如何取舍呢?这里提供些简单地判断规则,

    • 如果服务需要支持One Way Messaging/Message Queue/Duplex Communication,选择WCF
    • 如果服务需要在TCP/Named Pipes/UDP (wcf 4.5),选择WCF
    • 如果服务需要在http协议上,并且希望利用http协议的各种功能,选择Web API
    • 如果服务需要被各种客户端(特别是移动客户端)调用,选择Web API

    Web API 实战 (Web API + MongoDB + knockoutjs)

    ASP.NET网站上有很多简单的Web API实例,看看贴图和实例代码你就明白怎么用了。这里我们通过一个稍微复杂一点的实例来展示下Web API的功能。

    涉及技术

    在我们的实例里面用到了:

    服务URI Pattern

    Action Http verb URI
    Get contact list GET /api/contacts
    Get filtered contacts GET /api/contacts?$top=2
    Get contact by ID GET /api/contacts/id
    Create new contact POST /api/contacts
    Update a contact PUT /api/contacts/id
    Delete a contact DELETE /api/contacts/id

    准备工作

    1. 下载并安装Mongo DB,步骤看这里

    2. Mongo DB C# driver下载可以在nuget搜索mongocsharpdriver。

    3. 如果想本地察看数据库中内容,下载MongoVUE

    4. Knockoutjs下载可以在nuget搜索knockoutjs。

    代码实现

    1. 创建项目

    创建MVC4 Web Application

    1

    在Project Template中选择Web API

    2

    然后项目就创建成了,Controllers里面有一个ValuesController,是自动生成的一个最简单的Web API Controller。

    正如我们前面所说,里面引用的是System.Web.Http命名空间。

    3

    2. 创建model

    在model里面添加Contact类

    4

    代码如下,其中BsonId需要mongocsharpdriver。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Contact
        {
            [BsonId]
            public string Id { get; set; }
            public string Name { get; set; }
            public string Phone { get; set; }
            public string Email { get; set; }
            public DateTime LastModified { get; set; }
        }

    我们需要添加mongosharpdriver。

    7

    8

    另外我们需要在Model中添加Repository,Controller通过该类来访问Mongo DB。

    1
    2
    3
    4
    5
    6
    7
    public interface IContactRepository {
            IEnumerable GetAllContacts();
            Contact GetContact(string id);
            Contact AddContact(Contact item);
            bool RemoveContact(string id);
            bool UpdateContact(string id, Contact item);  
        }

    ContactRepository的完整实现如下,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    public class ContactRepository : IContactRepository
        {
            MongoServer _server = null;
            MongoDatabase _database = null;
            MongoCollection _contacts = null;
     
            public ContactRepository(string connection)
            {
                if (string.IsNullOrWhiteSpace(connection))
                {
                    connection = "mongodb://localhost:27017";
                }
     
                _server = new MongoClient(connection).GetServer();
                _database = _server.GetDatabase("Contacts");
                _contacts = _database.GetCollection("contacts");
     
                // Reset database and add some default entries
                _contacts.RemoveAll();
                for (int index = 1; index < 5; index++)
                {
                    Contact contact1 = new Contact
                    {
                        Email = string.Format("test{0}@example.com", index),
                        Name = string.Format("test{0}", index),
                        Phone = string.Format("{0}{0}{0} {0}{0}{0} {0}{0}{0}{0}", index)
                    };
                    AddContact(contact1);
                }
            }
     
            public IEnumerable GetAllContacts()
            {
                return _contacts.FindAll();
            }
     
            public Contact GetContact(string id)
            {
                IMongoQuery query = Query.EQ("_id", id);
                return _contacts.Find(query).FirstOrDefault();
            }
     
            public Contact AddContact(Contact item)
            {
                item.Id = ObjectId.GenerateNewId().ToString();
                item.LastModified = DateTime.UtcNow;
                _contacts.Insert(item);
                return item;
            }
     
            public bool RemoveContact(string id)
            {
                IMongoQuery query = Query.EQ("_id", id);
                WriteConcernResult result = _contacts.Remove(query);
                return result.DocumentsAffected == 1;
            }
     
            public bool UpdateContact(string id, Contact item)
            {
                IMongoQuery query = Query.EQ("_id", id);
                item.LastModified = DateTime.UtcNow;
                IMongoUpdate update = Update
                    .Set("Email", item.Email)
                    .Set("LastModified", DateTime.UtcNow)
                    .Set("Name", item.Name)
                    .Set("Phone", item.Phone);
                WriteConcernResult result = _contacts.Update(query, update);
                return result.UpdatedExisting;
            }
        }

    3. 添加Controller

    右键Controllers目录选择添加Controller

    5

    选择Empty API controller,将Controller命名为ContactsController

    6

    添加如下代码,可以看到Controller中的API方法名就是以http verb命名的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class ContactsController : ApiController
        {
            private static readonly IContactRepository _contacts = new ContactRepository(string.Empty);
     
            public IQueryable Get()
            {
                return _contacts.GetAllContacts().AsQueryable();
            }
     
            public Contact Get(string id)
            {
                Contact contact = _contacts.GetContact(id);
                if (contact == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
     
                return contact;
            }
     
            public Contact Post(Contact value)
            {
                Contact contact = _contacts.AddContact(value);
                return contact;
            }
     
            public void Put(string id, Contact value)
            {
                if (!_contacts.UpdateContact(id, value))
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
            }
     
            public void Delete(string id)
            {
                if (!_contacts.RemoveContact(id))
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
            }
        }

    4. 添加View

    首先添加Knockoutjs库,

    9

    Knockoutjs通过MVVM模式来实现动态html绑定数据,如下图,其中View-Model是客户端的javascript object保存的model数据。

    webapi_ef16

    先打开HomeController,里面添加一个新的Action代码如下,因为我们要在MVC中对于ContactsController添加对应的View。

    1
    2
    3
    4
    5
    6
    7
    public ActionResult Admin()
            {
                string apiUri = Url.HttpRouteUrl("DefaultApi", new { controller = "contacts", });
                ViewBag.ApiUrl = new Uri(Request.Url, apiUri).AbsoluteUri.ToString();
     
                return View();
            }

    然后右键Admin方法,选择添加View

    10

    选择Create strongly-typed view,在model class中选择Contact类。

    11

    添加View的完整代码,注意view中我们通过js去访问WebAPI,以及通过动态绑定将数据呈现在网页上。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    @model WebAPIDemo.Models.Contact
     
    @{
        ViewBag.Title = "Admin";
    }
     
    @section Scripts {
      @Scripts.Render("~/bundles/jqueryval")
      <script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.3.0.js")"></script>
      <script type="text/javascript">
          function ProductsViewModel() {
              var self = this;
              self.products = ko.observableArray();
     
              var baseUri = '@ViewBag.ApiUrl';
     
              self.create = function (formElement) {
                  // If valid, post the serialized form data to the web api
                  $(formElement).validate();
                  if ($(formElement).valid()) {
                      $.post(baseUri, $(formElement).serialize(), null, "json")
                          .done(function (o) { self.products.push(o); });
                  }
              }
     
              self.update = function (product) {
                  $.ajax({ type: "PUT", url: baseUri + '/' + product.Id, data: product });
              }
     
              self.remove = function (product) {
                  // First remove from the server, then from the UI
                  $.ajax({ type: "DELETE", url: baseUri + '/' + product.Id })
                      .done(function () { self.products.remove(product); });
              }
     
              $.getJSON(baseUri, self.products);
          }
     
          $(document).ready(function () {
              ko.applyBindings(new ProductsViewModel());
          })
      </script>
    }
     
    <h2>Admin</h2>
    <div class="content">
        <div class="float-left">
        <ul id="update-products" data-bind="foreach: products">
     
            <li>
                <div>
                    <div class="item">ID</div> <span data-bind="text: $data.Id"></span>
                </div>
                <div>
                    <div class="item">Name</div>
                    <input type="text" data-bind="value: $data.Name"/>
                </div>
                <div>
                    <div class="item">Phone</div>
                    <input type="text" data-bind="value: $data.Phone"/>
                </div>
                <div>
                    <div class="item">Email</div>
                    <input type="text" data-bind="value: $data.Email"/>
                </div>
                <div>
                    <div class="item">Last Modified</div> <span data-bind="text: $data.LastModified"></span>
                </div>
                <div>
                    <input type="button" value="Update" data-bind="click: $root.update"/>
                    <input type="button" value="Delete Item" data-bind="click: $root.remove"/>
                </div>
            </li>
        </ul>
        </div>
     
        <div class="float-right">
        <h2>Add New Product</h2>
        <form id="addProduct" data-bind="submit: create">
            @Html.ValidationSummary(true)
            <fieldset>
                <legend>Contact</legend>
                @Html.EditorForModel()
                <p>
                    <input type="submit" value="Save" />
                </p>
            </fieldset>
        </form>
        </div>
    </div>

    接下来在_layout.cshtml中添加一个admin页面的链接如下

    1
    2
    3
    4
    5
    6
    <ul id="menu">
        <li>@Html.ActionLink("Home", "Index", "Home", new { area = "" }, null)</li>
        <li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>
        <li>@Html.ActionLink("Admin", "Admin", "Home")</li>
     
    </ul>

    5. 测试与调试

    大功告成,直接运行下我们的作品,我们的admin链接也显示在右上角,

    12

    Admin页面的样子,Contact list是动态加载进来的,可以通过这个页面做添加,修改,删除的操作。

    13

    通过IE network capture来查看请求内容,

    重新加载页面,可以看到回复的格式为JSON,

    14

    JSON内容就是我们mock的一些数据。

    image

    接下来我们修改,删除,又添加了一条记录,可以看到使用了不同的http method。

    image

    通过前面安装的mongovue来查看下DB种的数据,先添加的user也在其中,令我感到欣慰。。。

    image

    其实还有两个有趣的实例,不过文章一写就长了,不好意思耽误大家时间,只好先放放,以后再写。

  • 相关阅读:
    【Robot Framework】List 的相关使用方法
    robot framework ——关键字run keyword if 如何在一个条件下接多个执行语句,以及如何写复杂条件句-关键字Run Keywords和and
    Robotframework之页面元素操作功能
    selenium之 下拉选择框Select
    selenium修改readonly属性的元件
    Robotframework之Python的自定义库
    python 元类详解
    从<<JavaScript权威指南>>抄下来的一个例子
    链接
    Python+selenium之获取文本值和下拉框选择数据
  • 原文地址:https://www.cnblogs.com/guyun/p/4589115.html
Copyright © 2020-2023  润新知