TinyWebServer,ASP.NET 项目开发的理想便携式 Web 服务器






4.61/5 (18投票s)
一个名为 TinyWebServer 的小型便携式 Web 服务器,可在 IIS 不可用之处使用。
引言
您是否曾想过在 IIS 不可用的情况下展示 ASP.NET 项目?您是否曾想过是否有一种方法可以将您的 Web 服务器随身携带,例如放在 USB 闪存驱动器甚至 CD 上?
如果是这样,那么这款很棒的 TinyWebServer 就是为您准备的!这款小型便携式 Web 服务器可用于在 IIS 不可用的地方测试和开发 ASP.NET 项目。
背景
我经常寻找一种无需安装 IIS 即可运行 ASP.NET 项目的方法。我曾想知道 Microsoft Visual Studio 2005 如何运行其 ASP.NET Development Webserver 而不受 IIS 的限制。我尝试了更多来寻找解决方案……我让 Google 因我的搜索无结果而生气,但仍然没有解决方案。因此,我决定找到一种编写自己的方法……
如您所知,VS2005 包含一个名为“ASP.NET Development Server”的优秀开发服务器。如果您查看它的实际工作原理,您会在 .NET 2.0 安装文件夹中找到一个名为 WebDev.WebServer.EXE 的控制台应用程序,通常位于“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727”。如果您运行它,您会看到类似这样的内容
如您所见,有一些命令行选项可以设置,以便在您指定的文件夹上运行 ASP.NET Development Server。好吧,看起来不错,但仍然存在一些问题
- 每次想再次运行时,您都应该传递指定的参数,或者您应该创建一个批处理文件来为您执行此操作。应该有更好的方法!
- 在基于 GUI 的平台上使用像这样的控制台应用程序并不那么有趣。应该有更好的方法!
- 尽管 VS2005 会找到一个免费可用的端口来为您运行 Web 服务器,但此控制台应用程序会从您传递的参数中获取它,这意味着您在命令行或批处理文件中指定的端口不一定可用。
- 此控制台应用程序使用一个名为 WebDev.WebHost.dll 的组件,该组件已在 GAC (Global Assembly Cache) 中注册。由于您看不到它,并且它不会出现在您的搜索结果中,因此它无法像您可能认为的那样使用 XCOPY 复制!
那么我们能做什么呢?
TinyWebServer 概览
服务器的主要部分是一个漂亮的小组件,名为 WebDev.WebHost.dll,我们将尝试访问它。这个小 DLL 位于以下目录
c:\WINDOWS\assembly\GAC_32\WebDev.WebHost\8.0.0.0__b03f5f7f11d50a3a\
当您尝试通过 Explorer 访问此路径时,您会发现 Explorer 由于安装 Microsoft .NET 框架时安装的 GAC 的 Explorer 扩展而无法轻松显示该文件夹。
那又怎样?很容易尝试通过命令提示符访问此路径?找到了吗?好的!将其复制到可访问的路径并执行以下操作……
启动 Microsoft Visual Studio 2005,创建一个新的 Windows Forms VB.NET 项目,添加对您复制的 DLL 的引用,然后创建一个名为 WebSite
的类。
Imports Microsoft.VisualStudio.WebHost
Imports System.IO
Public Class WebSite
Inherits Server
Implements IDisposable
' ...
我继承自 Server
的这个类,是用于访问我正在寻找的 Web 服务器的主要类。该类提供了一个带以下参数的构造函数:Port
[您将用于您站点的端口]、PhysicalPath
[用于指定您的站点所在的文件夹]、VirtualPath
[用作您的网站虚拟根],以及用于启动和停止服务的 Start
和 Stop
方法。
如您所见,主要思想和工具就是上面提到的,所有其他事情都是为了 GUI,查找随机可用的端口,加载和保存配置到 XML 文件等等。
关注点
- 在我开发过程中遇到的一个问题是,我的
WebSite
类继承的Server
类没有任何可写属性来设置Port
、PhysicalPath
和VirtualPath
。它们都是只读属性! - 另一个有趣的事情是我如何找到一个随机可用的端口,以及如何检查指定端口的可用性
但我需要能够在我需要时更改我定义的特定网站。为了解决这个问题,我决定创建一个名为 WebSiteData
的影子类,该类是可序列化的,并且可以包含 WebSite
中的所有属性,以及一些其他属性,如 GUID
和一个名为 Enabled
的属性来存储网站的启动/停止状态。
Imports System.Net
Imports System.Net.Sockets
Public Class PortHelper
Public Shared Function RandomPortAvailable() As Integer
Dim _portAvailable As Boolean = False
Dim _port As Integer = 0
Dim _listener As TcpListener = Nothing
Dim _rnd As New Random(Now.Millisecond)
While Not _portAvailable
_port = _rnd.Next(1001, 1999)
Dim _ipEntry As IPHostEntry = _
Dns.GetHostEntry(Environment.MachineName)
Dim _ipAddr() As IPAddress = _ipEntry.AddressList
Try
_listener = New TcpListener(_ipAddr(0), _port)
_listener.Start()
_portAvailable = True
_listener.Stop()
Catch ex As Exception
_portAvailable = False
End Try
End While
Return _port
End Function
Public Shared Function IsPortAvailable(ByVal iPort As Integer) _
As Boolean
Dim _listener As TcpListener = Nothing
Dim _ipEntry As IPHostEntry = _
Dns.GetHostEntry(Environment.MachineName)
Dim _ipAddr() As IPAddress = _ipEntry.AddressList
Try
_listener = New TcpListener(_ipAddr(0), iPort)
_listener.Start() : _listener.Stop()
Return True
Catch ex As Exception
Return False
End Try
End Function
End Class
PropertyGrid
对象,所以我正在寻找一种方法来使用户能够通过 PropertyGrid
界面随机查找并选择一个空闲端口。如您所见,UITypeEditor
继承类就是结果。Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Imports System.ComponentModel
Public Class PortEditor
Inherits UITypeEditor
Public Overrides Function GetEditStyle _
(ByVal context As ITypeDescriptorContext) _
As System.Drawing.Design.UITypeEditorEditStyle
Return UITypeEditorEditStyle.Modal
End Function
Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, _
ByVal provider As System.IServiceProvider, _
ByVal value As Object) As Object
Return PortHelper.RandomPortAvailable
End Function
End Class
以及我定义 Port
属性的 WebSiteData
类的一部分
<Category("Service")> _
<Description("Specifies the port, on which service will be available.")> _
<Editor(GetType(PortEditor), GetType(System.Drawing.Design.UITypeEditor))> _
Public Property Port() As Integer
Get
Return _port
End Get
Set(ByVal value As Integer)
If PortHelper.IsPortAvailable(value) Then
_port = value
If _name.IndexOf("WebSite on Port:") > -1 Then
Me.Name = String.Format("WebSite on Port:{0}", _port)
End If
Else
MessageBox.Show(String.Format("The port you specified, {0}, " & _
"is busy by another process. try another.", value), _
"Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning, _
MessageBoxDefaultButton.Button1)
End If
End Set
End Property
历史
- 版本 1.0.0.1:2007 年 11 月 17 日