脱离IIS,.NET WebApi的自托管(Self-Hosting)之路

momo314相同方式共享非商业用途署名转载

刚开始接触 .NET MVC.NET WebApi 的时候,我那叫一个纠结啊,你说,长这么像的两个东西,为什么不合并到一块去呢?而且,你造吗,命名空间不一样,简直害死人啊有木有!但是,那时候还是too young,虽然纠结,但是也没有深究过原因。

好吧,其实现在也没有╮( ̄▽ ̄)╭

好吧,说重点,突然有一天我就发现WebApi这玩意可是自托管哒!完全用不着什么IIS啊!

那么,到底怎么玩呢?我们先来吐槽一下:什么轻量级WCF,还有System.Net的命名空间早就应该看出点什么了啊喂!

第一步 新建一个控制台应用程序

并添加WebApi相关引用,注意,添加之后会默认帮你添加 System.Web.Http.WebHost 的引用,不过,折并没有什么鸟用,干掉他,然后手动添加引用 System.Web.Http.SelfHost

新建项目并添加相关引用新建项目并添加相关引用

第二步 假装我们在开发一个正常的WebApi程序

嗯,现在我们需要一个Controller了:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;
using WebApi.SelfHosting.Demo.Models;

namespace WebApi.SelfHosting.Demo.Controllers
{
    public class ProductsController : ApiController
    {
        Product[] products = new Product[]
        {
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
        };

        /// <summary>
        /// [/api/products]  Get a list of all products.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        /// <summary>
        /// [/api/products/id]  Get a product by ID.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Product GetProductById(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return product;
        }

        /// <summary>
        /// [/api/products/?category=category]  Get a list of products by category.
        /// </summary>
        /// <param name="category"></param>
        /// <returns></returns>
        public IEnumerable<Product> GetProductsByCategory(string category)
        {
            return products.Where(p => string.Equals(p.Category, category,
                    StringComparison.OrdinalIgnoreCase));
        }
    }
}

嗯, Product 类在这里:

namespace WebApi.SelfHosting.Demo.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

第三步 注册路由

嗯。。。继续假装在写一个正常的 WebApi ...

可是,人家是在Global里面注册路由的啊,咱们这玩意没有Global啊!没关系,咱们有Main函数啊,都是程序入口,应该差不多,呵呵。。。

using System;
using System.Web.Http;
using System.Web.Http.SelfHost;

namespace WebApi.SelfHosting.Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            var config = new HttpSelfHostConfiguration("http://localhost:8080");

            config.Routes.MapHttpRoute(
                "API Default", 
                "api/{controller}/{id}",
                new { id = RouteParameter.Optional });

            using (HttpSelfHostServer server = new HttpSelfHostServer(config))
            {
                server.OpenAsync().Wait();
                Console.WriteLine("Server Listening at 8080...");
                Console.WriteLine("Press Enter to quit.");
                Console.ReadLine();
            }
        }
    }
}

注意:

  1. 基于WebHost的WebApi的路由是注册到 HttpConfiguration 里的,但是基于SelfHost的却是注册到 HttpSelfHostConfiguration : 看吧,很明显,多了个SelfHost
  2. 基于WebHost的WebApi一般使用IIS作为服务器,但是SelfHost,自托管,顾名思义,需要自己实现一个服务器,有点Nodejs的意思。

第四步 搞定,打开浏览器看看效果吧

新建项目并添加相关引用新建项目并添加相关引用


补充: 有人表示编译的时候会报下面的异常:

类型“System.Web.Http.SelfHost.HttpSelfHostConfiguration”违反了继承安全性规则。派生类型必须与基类型的安全可访问性匹配或者比基类型的安全可访问性低。

搜索引擎给出的解决方法是修改 AssemblyInfo.cs 文件,手动指定程序集的透明级别。

其实并不是:多半是因为你引用的 System.Web.Http.SelfHost 程序集与你添加的WebApi版本不符造成的 (๑¯ω¯๑)


示例代码下载

✎﹏ 本文来自于 momo314和他们家的猫,文章原创,转载请注明作者并保留原文链接。