写在前面:

IIS是Windows平台非常关键的组件,它是微软自带的Web服务器,可以很方便的帮助我们运行起一个网站,WebApi等服务,提供给外部来访问。即使它被很多java或者ruby的同学各种鄙视,被.Net平台的同学们吐槽性能不好,不够灵活,部署受限等等,它依然在默默的帮助我们非常轻松的构建起一个Web应用。在.Net Core中微软提供了更为强大的Web服务器 Kestrel ,它 是一个跨平台ASP.NET Core 的 web 服务器基于libuv,跨平台的异步 I/O 库。它可以单独使用来host一个web服务,也可以与反向代理服务器(如 IIS、Nginx 或 Apache)结合使用。 反向代理服务器接收到来自 Internet 的 HTTP 请求,并在进行一些初步处理后将这些请求转发到 Kestrel。

那么今天我们来聊一聊另外的两种可以self host的解决方案:

第一种方式:Owin

Owin 是 Open Web Interface for .NET 的简称,从字面意思解释可以看出OWIN是针对.NET平台的开放Web接口。那Web接口是谁和谁之间的接口呢?是Web应用程序与Web服务器之间的 接口,OWIN就是.NET Web应用程序与Web服务器之间的接口。为什么需要这样一个接口呢?因为.NET Web应用程序是运行于Web服务器之中的,.NET Web应用程序需要通过Web服务器接收用户的请求,并且通过Web服务器将响应内容发送用户。如果没有这样一个接口,.NET Web应用程序就要依赖于所运行的具体Web服务器,比如ASP.NET应用程序要依赖于IIS。有了这个接口,ASP.NET应用程序只需依赖这个抽象接口,不用关心所运行的Web服务器。所以我们可以得出下面的结论:

OWIN的作用就是通过引入一组抽象接口,解耦了.NET Web应用程序与Web服务器,再次体现了接口的重要性。

而我们知道在软件开发中,每次解耦都是一次很大的进步。

更近一层我们可以理解为:OWIN是对ASP.NET Runtime的抽象。它将应用与服务器解耦, 使得便携式 .NET Web 应用以及跨平台的愿望成为现实, 标准的 OWIN 应用可以在任何OWIN 兼容的服务器上运行,不再依赖与 Windows 和 IIS,我们更可以不用装一大堆笨重的IDE(如 visual studio)来开发web应用程序,也不再那么的依赖于IIS去Host我们的程序。 我们可以用下面的一张图来表示它究竟可以做什么:

具体使用如下:

新建EventsController 继承自:System.Web.Http.ApiController

public class EventsController : ApiController
{
[Authorize]
[Route("events")]
public IEnumerable<Event> Get()
{
return GetAllEventsFromRepo();
} [Route("events/{id}")]
public Event GetById(Guid id)
{
return GetAllEventsFromRepo().First(x => x.EventId == id);
} [Route("events")]
public IEnumerable<Event> GetByType(string type)
{
return GetAllEventsFromRepo().Where(x => x.EventType.Equals(type, StringComparison.InvariantCultureIgnoreCase));
} [Route("events")]
public HttpResponseMessage Post(Event @event)
{
if (@event == null)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
} return new HttpResponseMessage(HttpStatusCode.Created);
} private IEnumerable<Event> GetAllEventsFromRepo()
{
return new List<Event>
{
new Event
{
EventId = Guid.Parse("45D80D13-D5A2-48D7-8353-CBB4C0EAABF5"),
Timestamp = DateTime.Parse("2014-06-30T01:37:41.0660548"),
EventType = "SearchView"
},
new Event
{
EventId = Guid.Parse("83F9262F-28F1-4703-AB1A-8CFD9E8249C9"),
Timestamp = DateTime.Parse("2014-06-30T01:37:52.2618864"),
EventType = "DetailsView"
},
new Event
{
EventId = Guid.Parse("3E83A96B-2A0C-49B1-9959-26DF23F83AEB"),
Timestamp = DateTime.Parse("2014-06-30T01:38:00.8518952"),
EventType = "SearchView"
}
};
}
}

然后新建一个Startup.cs的class,我们可以看到这里体现了Middleware(中间件)的思想,即插即用,熟悉.Net Core的同学的对它并不陌生。

public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes(); app.UseWebApi(config); var builder = new ContainerBuilder();
builder.RegisterApiControllers(typeof(EventsController).Assembly);
var container = builder.Build(); app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
}
}

上面代码中的ContainerBuilder 是Autofac提供的功能,它可以让我们动态的注册Controller到容器中,还有一个非常重要的东西就是 HttpConfiguration,它用来表示 HttpServer 实例的配置。

然后我们只需要下面一句代码就可以让我们API 工作起来了:

WebApp.Start<TestStartup>("http://localhost:51502")

这样通过 http://localhost:51502 地址就可以访问我们的服务了,非常的简单。

第二种方式:通过进程直接调用iisexpress.exe

iisexpress.exe我们很熟悉,它是windows平台自带的IIS 的运行文件,默认路径在: C:\Program Files\IIS Express 目录下,我们可以在代码中创建进程运行起这个exe就可以了。具体代码如下:

public class IISExpress : IDisposable
{
/// <summary>
/// Stores whether this instance has been disposed.
/// </summary>
private bool _isDisposed; /// <summary>
/// Stores the IIS Express process.
/// </summary>
private Process _process; /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} /// <summary>
/// Starts IIS Express using the specified directory path and port.
/// </summary>
/// <param name="directoryPath">
/// The directory path.
/// </param>
/// <param name="port">
/// The port.
/// </param>
/// <param name="address">
/// The address.
/// </param>
public void Start(string directoryPath, int port, Uri address)
{
if (_process != null)
{
throw new InvalidOperationException("The IISExpress process is already running.");
} if (address != null)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(address);
var webResponse = (HttpWebResponse)request.GetResponse(); if (webResponse.StatusCode == HttpStatusCode.OK)
{
return;
}
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
} var iisExpressPath = DetermineIisExpressPath();
var arguments = string.Format(CultureInfo.InvariantCulture, "/path:\"{0}\" /port:{1}", directoryPath, port); var info = new ProcessStartInfo(iisExpressPath)
{
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = true,
LoadUserProfile = true,
CreateNoWindow = false,
UseShellExecute = false,
Arguments = arguments
}; var startThread = new Thread(() => StartIisExpress(info))
{
IsBackground = true
}; startThread.Start();
} /// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
} if (disposing)
{
if (_process != null)
{
// Free managed resources
if (_process.HasExited == false)
{
SendStopMessageToProcess(_process.Id);
_process.Close();
} _process.Dispose();
}
} // Free native resources if there are any
_isDisposed = true;
} /// <summary>
/// Determines the IIS express path.
/// </summary>
/// <returns>
/// A <see cref="String" /> instance.
/// </returns>
private static string DetermineIisExpressPath()
{
string iisExpressPath; if (Environment.Is64BitOperatingSystem)
{
iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
}
else
{
iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
} iisExpressPath = Path.Combine(iisExpressPath, @"C:\Program Files\IIS Express\iisexpress.exe"); return iisExpressPath;
} /// <summary>
/// The send stop message to process.
/// </summary>
/// <param name="processId">
/// The process id.
/// </param>
private static void SendStopMessageToProcess(int processId)
{
try
{
for (var ptr = NativeMethods.GetTopWindow(IntPtr.Zero);
ptr != IntPtr.Zero;
ptr = NativeMethods.GetWindow(ptr, ))
{
uint num;
NativeMethods.GetWindowThreadProcessId(ptr, out num);
if (processId == num)
{
var handle = new HandleRef(null, ptr);
NativeMethods.PostMessage(handle, 0x12, IntPtr.Zero, IntPtr.Zero);
return;
}
}
}
catch (ArgumentException)
{
}
} /// <summary>
/// Starts the IIS express.
/// </summary>
/// <param name="info">
/// The info.
/// </param>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Required here to ensure that the instance is disposed.")]
private void StartIisExpress(ProcessStartInfo info)
{
try
{
_process = Process.Start(info); _process.WaitForExit();
}
catch (Exception)
{
Dispose();
}
} /// <summary>
/// The native methods.
/// </summary>
private static class NativeMethods
{
/// <summary>
/// The get top window.
/// </summary>
/// <param name="hWnd">
/// The h wnd.
/// </param>
/// <returns>
/// The <see cref="IntPtr"/>.
/// </returns>
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetTopWindow(IntPtr hWnd); /// <summary>
/// The get window.
/// </summary>
/// <param name="hWnd">
/// The h wnd.
/// </param>
/// <param name="uCmd">
/// The u cmd.
/// </param>
/// <returns>
/// The <see cref="IntPtr"/>.
/// </returns>
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); /// <summary>
/// The get window thread process id.
/// </summary>
/// <param name="hwnd">
/// The hwnd.
/// </param>
/// <param name="lpdwProcessId">
/// The lpdw process id.
/// </param>
/// <returns>
/// The <see cref="uint"/>.
/// </returns>
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId); /// <summary>
/// The post message.
/// </summary>
/// <param name="hWnd">
/// The h wnd.
/// </param>
/// <param name="Msg">
/// The msg.
/// </param>
/// <param name="wParam">
/// The w param.
/// </param>
/// <param name="lParam">
/// The l param.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}

代码比较简单,大家都能看得懂,我们只需要指定需要host的文件目录,访问端口,以及公开Uri地址就可以了,这样就能调用起IIS的服务,帮助我们host服务。

写在最后:

可能不仅限于这两种方式,我只是把我最近使用到的两种方式分享给出来,如果大家有更好的方式,欢迎交流分享。

Self Hosting WebServer 的几种方式的更多相关文章

  1. 盘点Mac上搭建本地WebServer的几种方式

    第一种: 通过Nginx搭建本地WebServer 安装nginx brew install nginx 安装完后在终端输入nginx指令,启动nginx查看效果 确定安装好之后,在根目录创建一个文件 ...

  2. WCF - Hosting WCF Service 四种托管方式

    https://www.tutorialspoint.com/wcf/wcf_hosting_service.htm After creating a WCF service, the next st ...

  3. .Net Self Hosting 的几种方式

    写在前面: IIS是Windows平台非常关键的组件,它是微软自带的Web服务器,可以很方便的帮助我们运行起一个网站,WebApi等服务,提供给外部来访问.即使它被很多java或者ruby的同学各种鄙 ...

  4. java中使用mongodb的几种方式

    最近有时间看了一下mongodb,因为mongodb更容易扩展所以考虑使用mongodb来保存数据. 首先下载安装mongodb,这是很简单的,装好后使用mongod命令就可以启动数据库.正式部署的话 ...

  5. System.Web.Http.Cors配置跨域访问的两种方式

    System.Web.Http.Cors配置跨域访问的两种方式 使用System.Web.Http.Cors配置跨域访问,众多大神已经发布了很多文章,我就不在详细描述了,作为小白我只说一下自己的使用心 ...

  6. iOS 登陆的实现四种方式

    iOS 登陆的实现四种方式 一. 网页加载: http://www.cnblogs.com/tekkaman/archive/2013/02/21/2920218.ht ml [iOS登陆的实现] A ...

  7. .net捕捉全局未处理异常的3种方式

    前言: 我们在实际项目开发中,经常会遇到一些不可预见的异常产生,有的异常在程序运行时就对其进行处理(try)但是,有的程序不需要每一个地方都用try进行处理,那么针对这种情况,可以参照下面的方式,实现 ...

  8. 根据服务端生成的WSDL文件创建客户端支持代码的三种方式

    第一种:使用wsimport是JDK自带的工具,来生成 生成java客户端代码常使用的命令参数说明: 参数 说明 -p 定义客户端生成类的包名称 -s 指定客户端执行类的源文件存放目录 -d 指定客户 ...

  9. C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路

    C#不用union,而是有更好的方式实现   用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...

随机推荐

  1. Windows下Mysql常用操作命令

    以下命令中的服务启动及停止需要使用以管理员身份运行cmd.exe. 1.启动Mysql服务:net start [mysql服务名].示例: net start mysql57 结果: 2.停止Mys ...

  2. Fastify 系列教程二 (中间件、钩子函数和装饰器)

    Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) 中间件 Fastify 提供了与 Express 和 Restify ...

  3. MongoDB理解

    1. 什么是MongoDB (1)MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. (2)MongoDB 是一个 ...

  4. Linux系统用户管理

    一.Linux账户 广义上讲,Linux的账户包括用户账户和组账户两种. Linux系统下的用户账户有两种,普通用户和超级用户.普通用户在系统中的任务就是普通工作,管理员在系统上的任务就是对普通用户和 ...

  5. 【20171025早】alert(1) to win 练习

    本人黑绝楼,自称老黑,男,25岁,曾经在BAT工作过两年,但是一直都是底层人员,整天做重复性工作,甚敢无趣,曾和工作十年之久的同事聊天,发现对方回首过往,生活是寡淡如水,只有机械性工作.旋即老黑毅然决 ...

  6. 原生js+css实现重力模拟弹跳系统的登录页面

    今天小颖把之前保存的js特效视频看了一遍,跟着视频敲了敲嘻嘻,用原生js实现一个炫酷的登录页面.怎么个炫酷法呢,看看下面的图片大家就知道啦. 效果图: 不过在看代码之前呢,大家先和小颖看看css中的o ...

  7. virualbox 安装 otter 必备软件

    前言 最近研究了一下阿里otter项目(分布式数据库同步),所以就在virualbox 上开始准备学习一下,遇到了不少坑,所以记录一下啊. otter 项目:https://github.com/al ...

  8. Java常用API

    常用Java API 一. java.io.BufferedReader类(用于从文件中读入一段字符:所属套件:java.io) 1. 构造函数BufferedReader(java.io.FileR ...

  9. d3根据数据绘制不同的形状

    绘制力导向图的时候通常节点都是圆形,但也会遇到公司节点绘制成圆型,人绘制成方形的情况,那我们怎么依据数据绘制不同的形状. 你可能首先会想到,这很简单啊,是公司的时候append circle,是人的时 ...

  10. HDU 5242 Game(三个贪心)

    Game Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...