Разработка распределенных приложений в Microsoft.NET Framework

         

II Использование ASPNET без IIS


В учебном процессе или при тестировании приложений иногда возникает потребность работы с веб службами ASP.NET без использования IIS. При использовании .NET Framework 2.0 и операционной системы Windows XP SP2 или Windows Server 2003 можно достаточно просто создать свой носитель веб служб на основе классов HttpListener и HttpRuntime, при этом служба IIS может быть не установлена в системе. Далее приведен пример простого класса, позволяющего осуществлять размещение приложений ASP.NET в учебных целях.

// SevaAspHost.cs using System; using System.IO; using System.NET; using System.Web; using System.Web.Hosting; using System.Text.RegularExpressions; namespace Seva.AspHost { public class AspHost: MarshalByRefObject { private HttpListener listener; private string virtualDir; private string hostingDir; public string VirtualDir { get {return virtualDir;} } public string HostingDir { get {return hostingDir;} } public static AspHost CreateHost(string[] prefixes, string aspVirtualDir, string aspHostingDir) { if (!HttpListener.IsSupported) { throw new NotSupportedException( "Требуется Windows XP SP2/Server 2003."); } AspHost host = (AspHost) ApplicationHost.CreateApplicationHost( typeof(AspHost), aspVirtualDir, aspHostingDir); host.Init(prefixes, aspVirtualDir, aspHostingDir); return host; } public void Init(string[] prefixes, string aspVirtualDir, string aspHostingDir) { virtualDir = aspVirtualDir; hostingDir = aspHostingDir; listener = new HttpListener(); foreach (string prefix in prefixes) listener.Prefixes.Add(prefix); } public void Start() { listener.Start(); } public void Stop() { listener.Stop(); } public void ProcessRequest() { HttpListenerContext context = listener.GetContext(); HttpListenerWorkerRequest workerRequest = new HttpListenerWorkerRequest(context, this); HttpRuntime.ProcessRequest(workerRequest); } } public class HttpListenerWorkerRequest: HttpWorkerRequest { private HttpListenerContext context; private AspHost host; public HttpListenerWorkerRequest(HttpListenerContext listenerContext, AspHost aspHost) { context = listenerContext; host = aspHost; } public override void EndOfRequest() { context.Response.OutputStream.Close(); context.Response.Close(); } public override void FlushResponse(bool finalFlush) { context.Response.OutputStream.Flush(); } public override string GetHttpVerbName() { return context.Request.HttpMethod; }

public override string GetHttpVersion() { return string.Format("HTTP/{0}", context.Request.ProtocolVersion.ToString()); } public override string GetLocalAddress() { return context.Request.LocalEndPoint.Address.ToString(); } public override int GetLocalPort() { return context.Request.LocalEndPoint.Port; } public override string GetQueryString() { return context.Request.Url.Query.TrimStart(new char[]{'?'}); } public override string GetRawUrl() { return context.Request.RawUrl; } public override string GetRemoteAddress() { return context.Request.RemoteEndPoint.Address.ToString(); } public override int GetRemotePort() { return context.Request.RemoteEndPoint.Port; } public override string GetUriPath() { return context.Request.Url.LocalPath; } public override void SendKnownResponseHeader(int index, string value) { context.Response.Headers[GetKnownResponseHeaderName(index)] = value; } public override void SendResponseFromMemory(byte[] data, int length) { context.Response.OutputStream.Write(data, 0, length); } public override void SendStatus(int statusCode, string statusDescription) { context.Response.StatusCode = statusCode; context.Response.StatusDescription = statusDescription; } public override void SendUnknownResponseHeader(string name, string value) { context.Response.Headers[name] = value; } public override void SendResponseFromFile(IntPtr handle, long offset, long length) { } public override void SendResponseFromFile(string filename, long offset, long length) { } public override string GetAppPath() { return host.VirtualDir; } public override string GetAppPathTranslated() { return host.HostingDir; } public override string GetUnknownRequestHeader(string name) { return context.Request.Headers[name]; } public override string GetKnownRequestHeader(int index) { switch (index) { case HeaderUserAgent: return context.Request.UserAgent; default: return context.Request.Headers[GetKnownRequestHeaderName(index)]; } } public override string GetFilePath() { string s = context.Request.Url.LocalPath; Regex re = new Regex(@"^(.*\.as\wx)\/\w+$"); Match m = re.Match(s); if (m.Success) s = m.Groups[1].Value; return s; } public override string GetFilePathTranslated() { string s = GetFilePath().Substring(host.VirtualDir.Length); return host.HostingDir + s.Replace('/', '\\'); } public override string GetPathInfo() { return context.Request.Url.LocalPath.Substring(GetFilePath().Length); } public override int ReadEntityBody(byte[] buffer, int size) { return context.Request.InputStream.Read(buffer, 0, size); } } } Листинг II.1.

Далее приведен пример использования этого класса, позволяющий хранить ASP страницы в поддиректории www и обращаться к ним по локальным адресам и порту 8080.

// AspServer.cs using System; using System.IO; using Seva.AspHost; class Program { static void Main(string[] args) { string[] prefixes = new string[] { "http://localhost:8080/", "http://127.0.0.1:8080/" }; AspHost host = AspHost.CreateHost(prefixes, "/", Directory.GetCurrentDirectory()+@"\www"); host.Start(); foreach (string s in prefixes) Console.WriteLine(s); while (true) host.ProcessRequest(); } }

Используемый при вызове метода ApplicationHost.CreateApplicationHost тип (в данном случае – AspHost) должен находиться, как один из вариантов, в глобальной сборке. Далее приводится make файл для создания простейшего носителя ASP.NET.

all: ASPServer.exe ASPServer.exe: *.cs AspHost.dll csc /out:ASPServer.exe AspServer*.cs /r:AspHost.dll AspHost.dll: SevaAspHost.cs AspHost.key csc /out:ASPHost.dll /t:library SevaAspHost.cs /keyfile:AspHost.key AspHost.key: sn -k AspHost.key install: gacutil -i ASPHost.dll if not exist www md www

После команды nmake && nmake install в поддиректорию www можно помещать страницы ASP.NET с расширениями aspx и asmx.



public class HttpListenerWorkerRequest: HttpWorkerRequest


// SevaAspHost.cs
using System;
using System.IO;
using System.NET;
using System.Web;
using System.Web.Hosting;
using System.Text.RegularExpressions;
namespace Seva.AspHost
{
public class AspHost: MarshalByRefObject
{
private HttpListener listener;

private string virtualDir;
private string hostingDir;

public string VirtualDir
{
get {return virtualDir;}
}

public string HostingDir
{
get {return hostingDir;}
}

public static AspHost CreateHost(string[] prefixes, string aspVirtualDir,
string aspHostingDir)
{
if (!HttpListener.IsSupported)
{
throw new NotSupportedException(
"Требуется Windows XP SP2/Server 2003.");
}
AspHost host = (AspHost) ApplicationHost.CreateApplicationHost(
typeof(AspHost), aspVirtualDir, aspHostingDir);
host.Init(prefixes, aspVirtualDir, aspHostingDir);
return host;
}
public void Init(string[] prefixes, string aspVirtualDir,
string aspHostingDir)
{
virtualDir = aspVirtualDir;
hostingDir = aspHostingDir;
listener = new HttpListener();
foreach (string prefix in prefixes)
listener.Prefixes.Add(prefix);
}

public void Start()
{
listener.Start();
}
public void Stop()
{
listener.Stop();
}

public void ProcessRequest()
{
HttpListenerContext context = listener.GetContext();

HttpListenerWorkerRequest workerRequest =
new HttpListenerWorkerRequest(context, this);

HttpRuntime.ProcessRequest(workerRequest);
}
}

public class HttpListenerWorkerRequest: HttpWorkerRequest
{
private HttpListenerContext context;
private AspHost host;


public HttpListenerWorkerRequest(HttpListenerContext listenerContext,
AspHost aspHost)
{
context = listenerContext;
host = aspHost;
}

public override void EndOfRequest()
{
context.Response.OutputStream.Close();
context.Response.Close();
}
public override void FlushResponse(bool finalFlush)
{
context.Response.OutputStream.Flush();
}
public override string GetHttpVerbName()
{
return context.Request.HttpMethod;
}
public override string GetHttpVersion()

// SevaAspHost.cs using System; using System.IO; using System.NET; using System.Web; using System.Web.Hosting; using System.Text.RegularExpressions; namespace Seva.AspHost { public class AspHost: MarshalByRefObject { private HttpListener listener; private string virtualDir; private string hostingDir; public string VirtualDir { get {return virtualDir;} } public string HostingDir { get {return hostingDir;} } public static AspHost CreateHost(string[] prefixes, string aspVirtualDir, string aspHostingDir) { if (!HttpListener.IsSupported) { throw new NotSupportedException( "Требуется Windows XP SP2/Server 2003."); } AspHost host = (AspHost) ApplicationHost.CreateApplicationHost( typeof(AspHost), aspVirtualDir, aspHostingDir); host.Init(prefixes, aspVirtualDir, aspHostingDir); return host; } public void Init(string[] prefixes, string aspVirtualDir, string aspHostingDir) { virtualDir = aspVirtualDir; hostingDir = aspHostingDir; listener = new HttpListener(); foreach (string prefix in prefixes) listener.Prefixes.Add(prefix); } public void Start() { listener.Start(); } public void Stop() { listener.Stop(); } public void ProcessRequest() { HttpListenerContext context = listener.GetContext(); HttpListenerWorkerRequest workerRequest = new HttpListenerWorkerRequest(context, this); HttpRuntime.ProcessRequest(workerRequest); } } public class HttpListenerWorkerRequest: HttpWorkerRequest { private HttpListenerContext context; private AspHost host; public HttpListenerWorkerRequest(HttpListenerContext listenerContext, AspHost aspHost) { context = listenerContext; host = aspHost; } public override void EndOfRequest() { context.Response.OutputStream.Close(); context.Response.Close(); } public override void FlushResponse(bool finalFlush) { context.Response.OutputStream.Flush(); } public override string GetHttpVerbName() { return context.Request.HttpMethod; } public override string GetHttpVersion() { return string.Format("HTTP/{0}", context.Request.ProtocolVersion.ToString()); } public override string GetLocalAddress() { return context.Request.LocalEndPoint.Address.ToString(); } public override int GetLocalPort() { return context.Request.LocalEndPoint.Port; } public override string GetQueryString() { return context.Request.Url.Query.TrimStart(new char[]{'?'}); } public override string GetRawUrl() { return context.Request.RawUrl; } public override string GetRemoteAddress() { return context.Request.RemoteEndPoint.Address.ToString(); } public override int GetRemotePort() { return context.Request.RemoteEndPoint.Port; } public override string GetUriPath() { return context.Request.Url.LocalPath; } public override void SendKnownResponseHeader(int index, string value) { context.Response.Headers[GetKnownResponseHeaderName(index)] = value; } public override void SendResponseFromMemory(byte[] data, int length) { context.Response.OutputStream.Write(data, 0, length); } public override void SendStatus(int statusCode, string statusDescription) { context.Response.StatusCode = statusCode; context.Response.StatusDescription = statusDescription; } public override void SendUnknownResponseHeader(string name, string value) { context.Response.Headers[name] = value; } public override void SendResponseFromFile(IntPtr handle, long offset, long length) { } public override void SendResponseFromFile(string filename, long offset, long length) { } public override string GetAppPath() { return host.VirtualDir; } public override string GetAppPathTranslated() { return host.HostingDir; } public override string GetUnknownRequestHeader(string name) { return context.Request.Headers[name]; } public override string GetKnownRequestHeader(int index) { switch (index) { case HeaderUserAgent: return context.Request.UserAgent; default: return context.Request.Headers[GetKnownRequestHeaderName(index)]; } } public override string GetFilePath() { string s = context.Request.Url.LocalPath; Regex re = new Regex(@"^(.*\.as\wx)\/\w+$"); Match m = re.Match(s); if (m.Success) s = m.Groups[1].Value; return s; } public override string GetFilePathTranslated() { string s = GetFilePath().Substring(host.VirtualDir.Length); return host.HostingDir + s.Replace('/', '\\'); } public override string GetPathInfo() { return context.Request.Url.LocalPath.Substring(GetFilePath().Length); } public override int ReadEntityBody(byte[] buffer, int size) { return context.Request.InputStream.Read(buffer, 0, size); } } }
Листинг II.1.
Закрыть окно




{
return string.Format("HTTP/{0}",
context.Request.ProtocolVersion.ToString());
}

public override string GetLocalAddress()
{
return context.Request.LocalEndPoint.Address.ToString();
}

public override int GetLocalPort()
{
return context.Request.LocalEndPoint.Port;
}

public override string GetQueryString()
{
return context.Request.Url.Query.TrimStart(new char[]{'?'});
}

public override string GetRawUrl()
{
return context.Request.RawUrl;
}

public override string GetRemoteAddress()
{
return context.Request.RemoteEndPoint.Address.ToString();
}

public override int GetRemotePort()
{
return context.Request.RemoteEndPoint.Port;
}

public override string GetUriPath()
{
return context.Request.Url.LocalPath;
}

public override void SendKnownResponseHeader(int index, string value)
{
context.Response.Headers[GetKnownResponseHeaderName(index)] = value;
}

public override void SendResponseFromMemory(byte[] data, int length)
{
context.Response.OutputStream.Write(data, 0, length);
}

public override void SendStatus(int statusCode, string statusDescription)
{
context.Response.StatusCode = statusCode;
context.Response.StatusDescription = statusDescription;
}

public override void SendUnknownResponseHeader(string name, string value)
{
context.Response.Headers[name] = value;
}

public override void SendResponseFromFile( IntPtr handle, long offset,
long length)
{
}

public override void SendResponseFromFile(string filename, long offset,
long length)
{
}
public override string GetAppPath()
{
return host.VirtualDir;
}

public override string GetAppPathTranslated()
{
return host.HostingDir;
}

public override string GetUnknownRequestHeader(string name)
{
return context.Request.Headers[name];
}

public override string GetKnownRequestHeader(int index)
{
switch (index)
{
case HeaderUserAgent:
return context.Request.UserAgent;
default:
return
context.Request.Headers[GetKnownRequestHeaderName(index)];
}


}

public override string GetFilePath()
{
string s = context.Request.Url.LocalPath;
Regex re = new Regex(@"^(.*\.as\wx)\/\w+$");
Match m = re.Match(s);
if (m.Success)
s = m.Groups[1].Value;
return s;
}

public override string GetFilePathTranslated()
{
string s = GetFilePath().Substring(host.VirtualDir.Length);
return host.HostingDir + s.Replace('/', '\\');
}
public override string GetPathInfo()
{
return context.Request.Url.LocalPath.Substring(GetFilePath().Length);
}

public override int ReadEntityBody(byte[] buffer, int size)
{
return context.Request.InputStream.Read(buffer, 0, size);
}
}
}