用WebAPI2( OWIN )实现RESTful service [一](基于convention)

环境:VS2017 , Win10 64bit ,

实现功能:

1. 提供index.html,能实现人机简单交互(通过JQuery)。复杂的可以考虑Angular1.x或ASP.Net的Single Page App,我还没实践。

2. 提供RESTful的web service,方便其他工具调用。场景例如:a. 你希望通知远程机器干掉某个process或获取机器信息 b. 在本机上,你希望从Windows Service发一些显示信息出来

方案:本案例是convention-based routing的实现。通过阅读Routing in ASP.NET Web API 与 Attribute Routing in ASP.NET Web API 2这两个例子,灵活性上和易理解上Attribute Routing > convention-based ,根据Routing and Action Selection in ASP.NET Web API,要完全理解convention-based如何选择controller与action反而非常吃力。推荐使用Attribute Routing.

效果图:

用浏览器访问http://localhost:9095/返回

更新(2021/01/23): 今天在梳理web service与web API概念。发现官方例子文档是webAPI2,就想难道还有webAPI3,4,5?和.Net或Core有什么关系? 目前也可以用Core实现webAPI ,步骤和.net很接近,只是在VS2019中有ASP.NET Core Web API template.

步骤:

1. 参考Use OWIN to Self-Host ASP.NET Web API,重复内容就不说了。要注意的是tutorial提供的sample只监听了localhost,本地通过Edge|IE|Chrome访问没问题,其他机器访问会报Error 400 , invalid hostname错误,所以我的Program.cs添加了监听所有9095端口的部分(参考了Hosting WebAPI using OWIN in a windows serviceHTTP Error 400 - OWIN with Topshelf)。

using Microsoft.Owin.Hosting;

namespace demo
{

static void Main()
        {
            StartOptions options = new StartOptions();
            options.Urls.Add("http://localhost:9095");//这3行保证了本机和其他机器都能访问
            options.Urls.Add("http://127.0.0.1:9095");
            options.Urls.Add("http://*:9095");
            // Start OWIN host 
            using (WebApp.Start<Startup>(options))
            {
                // Create HttpClient and make a request to api/values 
                HttpClient client = new HttpClient();

                var response = client.GetAsync( "http://localhost:9095/api/values/AllProducts").Result;

                Console.WriteLine(response);
                Console.WriteLine(response.Content.ReadAsStringAsync().Result);
                Console.ReadLine();
            }
        }
}

2. 提供index.html给客户端,参考How to serve index.html with web api selfhosted with OWIN ,所以我的Startup.cs有变化. 原文的链接文章提到了Katana,这原本是ASP.Net团队开发的OWIN实现2019年起又被整合回.Net Core了。

记得安装nuget包Microsoft.Owin.StaticFiles


using Owin;
using System.Web.Http;
using Microsoft.Owin.StaticFiles;
using Microsoft.Owin.FileSystems;
 public class Startup
    {
        // This code configures Web API. The Startup class is specified as a type
        // parameter in the WebApp.Start method.
        public void Configuration(IAppBuilder appBuilder)
        {
            // Configure Web API for self-host. 
            HttpConfiguration config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            appBuilder.UseWebApi(config);
            //下面这部分就是提供index.html的能力
            const string rootFolder = ".";
            var fileSystem = new PhysicalFileSystem(rootFolder);
            var options = new FileServerOptions
            {
                EnableDefaultFiles = true,
                FileSystem = fileSystem
            };
            appBuilder.UseFileServer(options);
        }
    }

3. Tutoral是没提供index.html,html我参考了Get Started with ASP.NET Web API 2 。创建index.html后记得设置,这样工程编译后才会把它放在debug/release目录下

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Product App</title>
</head>
<body>

    <div>
        <h2>All Products</h2>
        <ul id="products" />
    </div>
    <div>
        <h2>Search by ID</h2>
        <input type="text" id="prodId" size="5" />
        <input type="button" value="Search" onclick="find();" />
        <p id="product" />
    </div>

    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
    <script>
    var uri = 'api/values';
    var uri_rel_getAllProducts = "AllProducts";
    $(document).ready(function () {
      // Send an AJAX request
        $.getJSON(uri + "/" + uri_rel_getAllProducts)
          .done(function (data) {
            // On success, 'data' contains a list of products.
            $.each(data, function (key, item) {
              // Add a list item for the product.
              $('<li>', { text: formatItem(item) }).appendTo($('#products'));
            });
          });
    });

    function formatItem(item) {
      //return item.Name + ': $' + item.Price;
        return item;
    }
    var uri_rel_Product = "Product";
    function find() {
      var id = $('#prodId').val();
        $.getJSON(uri + "/" + uri_rel_Product + '/' + id)
          .done(function (data) {
            $('#product').text(formatItem(data));
          })
          .fail(function (jqXHR, textStatus, err) {
            $('#product').text('Error: ' + err);
          });
    }
    </script>
</body>
</html>

4. 默认tutoral的routing是使用了/api/{controller}/{id}形式,即系统收到Get请求默认就找Get开头的方法,不管方法名是什么。我为了调用清楚,参考了Routing in ASP.NET Web API ,所以我的Startup.cs里是 routeTemplate: "api/{controller}/{action}/{id}",同时Controller也略有不同

public class ValuesController : ApiController
    {
        //只适用于/api/{controller}/{id}的routing
        //由于我使用了/api/{controller}/{action}/{id}的routing,这部分不会被调用
        // GET api/values   
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

//适用于/api/{controller}/{action}/{id}的routing,客户端访问/api/values/AllProducts/{id}时会被调用
        [HttpGet]
        [ActionName("AllProducts")]
        public IEnumerable<string> AllProducts()
        {
            return new string[] { "value1", "value2" };
        }


        //适用于/api/{controller}/{action}/{id}的routing,客户端访问/api/values/Product/{id}时会被调用
        [HttpGet]
        [ActionName("Product")]
        public string Product(int id)
        {
            return "haha";
        }

        //只适用于/api/{controller}/{id}的routing
        //由于我使用了/api/{controller}/{action}/{id}的routing,这部分不会被调用
        // GET api/values/5 
        public string Get(int id)
        {
            return "value";
        }

        //只适用于/api/{controller}/{id}的routing
        //由于我使用了/api/{controller}/{action}/{id}的routing,这部分不会被调用
        // POST api/values 
        public void Post([FromBody]string value)
        {
        }

        //只适用于/api/{controller}/{id}的routing
        //由于我使用了/api/{controller}/{action}/{id}的routing,这部分不会被调用
        // PUT api/values/5 
        public void Put(int id, [FromBody]string value)
        {
        }

        //只适用于/api/{controller}/{id}的routing
        //由于我使用了/api/{controller}/{action}/{id}的routing,这部分不会被调用
        // DELETE api/values/5 
        public void Delete(int id)
        {
        }
    }

5. (Optional) 要是self-host的Webservice所在机器要开firewall,确保netsh http add urlacl url=http://*:9000/ user=<your user> 在被外部机器访问前被执行过。这个要求在2处都有提到Hosting WebAPI using OWIN in a windows service | OWIN-WebAPI-Service ,当然这是Microsoft官方也提及了Configuring HTTP and HTTPS ,不过是在WCF里提到的。

 

后续学习:从Microsoft的Create a REST API with Attribute Routing in ASP.NET Web API 2中我们能看到,Response的返回有返回IEnumerable的,也有返回IQueryable 。IQueryable是服务于oData和filter descriptor的,比如:我要查询100个工人,希望返回的结果是按照工龄排序了的。又或者,工人有很多,我希望查询到工龄最大的一个人。这些排序或过滤的功能,要是数据量大必须在Server端完成。

http://localhost:18950/api/products?$orderby=price desc

http://localhost:18950/api/products?$filter=name eq 'Gizmo 3'

http://localhost:18950/api/products?$orderby=price%20desc&$top=1

有个问题: 要是数据量大,理应数据库去完成过滤与排序,放在serverlet上是否合适?毕竟数据库很有可能是独立服务器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值