Vous en rêviez : Un client lourd DotNet peut héberger une WebApp complète sans avoir à installer IIS sur chaque poste.
Imaginez. Souvent, en entreprise, le schéma classique consiste en une base de donnée hébergée à l'entreprise, et des applications dites 'clients lourds' installées chez les commerciaux ou les clients. Généralement ces 'clients lourds' sont développés à l'aide d'une base Access, et avec une interface utilisateur constituée de WinForms (si ce n'est parfois directement en VBA).
Problème : Les interfaces à base de WinForm sont généralement laides, condensées, complexes, et incompréhensible pour un néophyte bien plus habitué à naviguer dans des interfaces Web. Je vous propose ici LA solution : Créer une 'WebSkin' à votre affreux 'client lourd' pour le rendre plus digeste à vos commerciaux ou clients. Profitez de la puissance de ASP.Net pour réaliser une interface claire, basée sur les pages qui pourront être données à de vrais Web Designers.
Grand habitué de csharpfr.com, ayant souvent trouvé des sources intéréssantes pour mes applications, sur ce sujet que je savais possible, je n'ai malheureusement trouvé aucune source. J'ai donc décidé de retrousser mes manches et de m'y coller moi-même. Je vous livre le résultat d'une petite semaine de travail, largement aidé par le livre référence "Pratique de .Net 2 et C#" de Patrick Smacchia, que je ne saurais que trop conseiller à tous les développeurs C#.
Cet exemple est réalisé avec Visual C# 2008 Express, et Visual Web Developper 2008 Express.
Cette technique est extrêmement puissante et permet d'héberger simplement la totalité d'une WebApp ASP.Net sur chaque poste ou est installée le 'client lourd', accessible en localhost sans connexion internet. On obtient donc une application déconnectée de la base entreprise, mais consultable localement en mode déconnecté à travers une belle interface Web.
Il y a un BUG sous Windows Vista qui bloque le HttpListener pour des questions de sécurités. Il doit y avoir un bout de code à rajouter pour que ca fonctionne sous Vista, mais lequel ? J'ai chercher un peu sur le web mais je n'ai rien trouvé.
Je ne suis pas Web Developper (bien que je vais devoir m'y mettre) donc l'exemple de la WebApp fournie est très sommaire. Juste pour vérifier que la technique fonctionne. A ce propos le code qui ne semble pas marcher, et c'est logique puisqu'on se passe de IIS, est la redirection de la page à la fin d'un traitement :
this.Response.Redirect("Welcome.aspx");//BUG in Local Server ASPNET
Il faut donc feinter et inclure une fonction javascript dans la page retournée à la fin du traitement qui fait la redirection directement depuis le browser de l'utilisateur. Le code ci-dessus est alors remplacé par :
//use body.onLoad
HtmlGenericControl body = this.FindControl("body") as HtmlGenericControl;
body.Attributes["onLoad"] = "RedirectWelcome()"; //nom de la fonction javascript
Laquelle fonction javascript est écrite directement dans la page aspx :
<script language="javascript" type="text/javascript">
function RedirectWelcome() {
window.location = "Welcome.aspx";
}
</script>
Voilà l'astuce. Pour le reste tout semble fonctionner normalement (avec IE).
Bonne lecture, et bon tests...
MAJ V2 V3:
J'ai du intégrer cette exemple dans mon application, et pour cela j'ai du revoir tout le code de l'exemple. J'ai donc fais une V2 et une V3, dont le résultat est une dll bien pratique qui doit être insérée dans le dossier /bin/ de votre WebApp. Puis il suffit d'implémenter la classe 'ServerASPNET' qui encapsule toute la complexité du fonctionnement du server ASPNET local. On peut même instancier plusieur 'ServerASPNET', un pour chaque WebApp, et ainsi constituer une ferme de WebApp. A partir de la pourquoi pas redevelopper un serveur Web complet compatible ASPNET ?
En particulier il doit être possible de pallier le BUG de redirection à la fin d'un traitement (voir le code).
MAJ V4:
J'ai ajouté quelques fonctionnalités indispensables :
- Gestion des sous-repertoires de la WebApp (dont App_GlobalRessources)
- Gestion des fichiers Non-ASP (images, css, html)
Source / Exemple :
using System;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using System.Windows.Forms;
namespace Peerocracy.PAPICS.Client.Beta0.Skin
{
/// <summary>
/// Class ServerASPNET that can start a thread for a Server ASPNET.
/// This can process one WebApp.
/// This can be closed to stop http listening.
/// </summary>
public class ServerASPNET : IDisposable
{
#region Properties
/// <summary>
/// Return true if the ServerASPNET is running.
/// </summary>
public bool IsStarted
{
get { return _isStarted; }
}
private bool _isStarted = false;
/// <summary>
/// The full path to the WebApp.
/// The WebApp must have a /bin/ folder that contains this dll.
/// </summary>
public string WebAppPath
{
get { return _WebAppPath; }
set
{
//check if not already started
if (this.IsStarted)
{
this.ConsoleWrite("Error: Cannot change Path of WebApp until server stopped.");
return;
}
//null value
if (value == null || !new DirectoryInfo(value).Exists)
{
this.ConsoleWrite("Error: the given WebAppPath folder not exist.");
return;
}
//affect
_WebAppPath = value;
}
}
private string _WebAppPath = null;
/// <summary>
/// The name of WebApp.
/// Use to build the URI to listen : http://IptoListen:PortToListen/WebAppName/.
/// </summary>
public string WebAppName
{
get { return _WebAppName; }
set
{
//check if not already started
if (this.IsStarted)
{
this.ConsoleWrite("Error: Cannot change Name of WebApp until server stopped.");
return;
}
//null value
if (value == null || value.Length == 0)
{
this.ConsoleWrite("Error: the given WebAppName is null.");
return;
}
//affect
_WebAppName = value;
}
}
private string _WebAppName = "WebAppName";
/// <summary>
/// This is the address IP to listen.
/// Use to build the URI to listen : http://IptoListen:PortToListen/WebAppName/.
/// Must be : 'localhost' or the public IP of this station.
/// See GetIPHostStation().
/// </summary>
public string IPtoListen
{
get { return _IPtoListen; }
set
{
//check if not already started
if (this.IsStarted)
{
this.ConsoleWrite("Error: Cannot change IP to listen until server stopped.");
return;
}
//null value
if (value == null || value.Length == 0)
value = "localhost";
//affect
_IPtoListen = value;
}
}
private string _IPtoListen = "localhost";
/// <summary>
/// This is the port of this station to listen http request.
/// Use to build the URI to listen : http://IptoListen:PortToListen/WebAppName/.
/// </summary>
public int PortToListen
{
get { return _PortToListen; }
set
{
//check if not already started
if (this.IsStarted)
{
this.ConsoleWrite("Error: Cannot change Port to listen until server stopped.");
return;
}
//null value
if (value <= 0) return;
//affect
_PortToListen = value;
}
}
private int _PortToListen = 8008;
/// <summary>
/// Define the URI to listen : http://IptoListen:PortToListen/WebAppName/.
/// Set: fill the properties : _IPtoListen, _PortToListen, _WebAppName.
/// </summary>
public string URItoListen
{
get
{
return "http://" + _IPtoListen + ":" + _PortToListen + "/" + _WebAppName + "/";
}
set
{
//null value
if (value == null || !value.StartsWith("http://"))
return;
//regex.Match
Match matchURI = new Regex(@"^http://(?'IPtoListen'[^:/]*):(?'PortToListen'[^/]*)/(?'WebAppName'.*?)/$", RegexOptions.Compiled).Match(value); ;
//affect properties
this.IPtoListen = matchURI.Groups["IPtoListen]"].Value;
this.PortToListen = Int32.Parse(matchURI.Groups["PortToListen"].Value);
this.WebAppName = matchURI.Groups["WebAppName"].Value;
}
}
/// <summary>
/// The thread that contains the _HostProcessor_ASPNET.
/// Init by fct StartThread_ServerASPNET().
/// </summary>
public Thread Thread_ServerASPNET
{
get { return _Thread_ServerASPNET; }
}
private Thread _Thread_ServerASPNET = null;
/// <summary>
/// Class of the host _HostProcessor_ASPNET.
/// Init by fct StartThread_ServerASPNET().
/// </summary>
public HostProcessorASPNET HostProcessor_ASPNET
{
get { return _HostProcessor_ASPNET; }
}
private HostProcessorASPNET _HostProcessor_ASPNET = null;
#endregion
#region Ctors
/// <summary>
/// Ctor, nothing.
/// </summary>
public ServerASPNET()
{
_timerUpdateConsoleProcessor = new System.Windows.Forms.Timer();
_timerUpdateConsoleProcessor.Interval = 1000; //1sec
_timerUpdateConsoleProcessor.Tick += new EventHandler(this.timerUpdateConsoleProcessor_Tick);
}
/// <summary>
/// Ctor with given uriToListen, and webAppPath.
/// </summary>
/// <param name="uriToListen">The URI to listen http requests.</param>
/// <param name="webAppPath">The full path of the WebApp.</param>
public ServerASPNET(string uriToListen, string webAppPath)
: this()
{
//affect
this.URItoListen = uriToListen;
this.WebAppPath = webAppPath;
}
/// <summary>
/// Ctor with given uriToListen, and webAppPath, and rtfConsole.
/// </summary>
/// <param name="uriToListen">The URI to listen http requests.</param>
/// <param name="webAppPath">The full path of the WebApp.</param>
/// <param name="rtfConsole">The RichTextBox control of the console.</param>
public ServerASPNET(string uriToListen, string webAppPath, RichTextBox rtfConsole)
: this(uriToListen, webAppPath)
{
//affect
this.ConsoleRtf = rtfConsole;
}
#endregion
#region Functions
/// <summary>
/// Return the list of the address IP of this Host Station.
/// </summary>
/// <returns>One string for each IP.</returns>
public string[] GetIPHostStation()
{
//get IPs
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
//make new string[]
string[] IPs = new string[ips.Length];
//fill new string[]
for (int i = 0; i < ips.Length; i++)
IPs[i] = ips[i].ToString();
//return
return IPs;
}
/// <summary>
/// Start the ServerASPNET.
/// Start a new background thread for the function fctRunThread_ServerASPNET.
/// </summary>
public void StartThread_ServerASPNET()
{
//check if not closed
if (_isDisposed) return;
//check if not already started
if (this.IsStarted)
{
this.ConsoleWrite("Local Server ASPNET already started !");
return;
}
//check if given WebApp path
if (_WebAppPath == null || !new DirectoryInfo(_WebAppPath).Exists)
throw new Exception("Error: Cannot start until given the path of WebAppPath property.");
//check if given WebApp name
if (_WebAppName == null || _WebAppName.Length == 0)
throw new Exception("Error: Cannot start until given the name of WebAppName property.");
//make new background thread
_Thread_ServerASPNET = new Thread(this.RunThread_ServerASPNET);
_Thread_ServerASPNET.IsBackground = true;
//start thread
_Thread_ServerASPNET.Start();
//wait for started
while (!this.IsStarted)
{
Application.DoEvents();
Thread.Sleep(10);
}
//console
if (this.ConsoleRtf != null)
_strConsoleBeforeConsoleProcessor = this.ConsoleRtf.Text;
_timerUpdateConsoleProcessor.Start();
}
/// <summary>
/// Stop the ServerASPNET.
/// Stop the thread of the ServerASPNET.
/// </summary>
public void StopThread_ServerASPNET()
{
//check if not closed
if (_isDisposed) return;
//check if server already stopped
if (!this.IsStarted)
{
this.ConsoleWrite("Local Server ASPNET already stopped !");
return;
}
try
{
//stop listening
_HostProcessor_ASPNET.StopHttpListening();
//_HostProcessor_ASPNET = null;
}
catch (Exception err)
{ }
//abort thread
_Thread_ServerASPNET.Abort();
//_Thread_ServerASPNET = null;
_isStarted = false;
//console
_timerUpdateConsoleProcessor.Stop();
this.UpdateConsoleFromProcessor();
this.ConsoleWrite("Stop: Server Aborted !");
}
/// <summary>
/// Run the ServerASPNET.
/// Call by fct StartThread_ServerASPNET();
/// Exec in new thread.
/// </summary>
private void RunThread_ServerASPNET()
{
//check if not closed
if (_isDisposed) return;
//console
//this.ConsoleClean();
this.ConsoleWrite("Start new Server ASPNET threaded on this station,");
this.ConsoleWrite(" for URI to listen : '" + this.URItoListen + "'.");
this.ConsoleWrite(" for WebApp : '" + _WebAppPath + "'.");
//search the dll containing the class 'HostProcessorASPNET'
//search in '_WebAppPath/bin/*.dll|exe'
_HostProcessor_ASPNET =
(HostProcessorASPNET)ApplicationHost.CreateApplicationHost(
typeof(HostProcessorASPNET), @"/", _WebAppPath); //BUG if not found this dll in '_WebAppPath/bin'
////subscribe on events
////BUG: because different 'Domain Application'.
//_HostProcessor_ASPNET.OnConsoleChanged +=
// new HostProcessorASPNET.ConsoleChangedEventHandler(_HostProcessor_ASPNET_OnConsoleChanged);
//_HostProcessor_ASPNET.OnIsStartedChanged +=
// new HostProcessorASPNET.IsStartedChangedEventHandler(_HostProcessor_ASPNET_OnIsStartedChanged);
//start http listening : lost control
this.ConsoleWrite("OK: Server ASPNET is started.");
//_strConsoleBeforeConsoleProcessor = this.ConsoleRtf.Text;
_isStarted = true;
_HostProcessor_ASPNET.StartHttpListening(this.URItoListen, this.WebAppPath);
//never reach
return;
}
//void _HostProcessor_ASPNET_OnIsStartedChanged(object sender, EventArgs args)
//{
// _isStarted = _HostProcessor_ASPNET.IsStarted;
//}
//void _HostProcessor_ASPNET_OnConsoleChanged(object sender, ConsoleChangedEventArgs args)
//{
// this.ConsoleWrite(args.NewLineText);
//}
/// <summary>
/// Start a new IE browser on the default page of the running WebApp.
/// </summary>
public void StartBrowserOnWebApp()
{
this.StartBrowserOnWebApp(null, true);
}
/// <summary>
/// Start a new browser on the given page of the running WebApp.
/// </summary>
/// <param name="page">The name of the page to show.</param>
/// <param name="isIEBrowser">True for open IE browser, or false for default browser.</param>
public void StartBrowserOnWebApp(string page, bool bIEBrowser)
{
//check if closed
if (_isDisposed) return;
//check if started
if (!this.IsStarted) return;
//null value
if (page == null) page = "";
//search page
if (page.StartsWith("/"))
page = page.Substring(1);
if (!page.StartsWith("http://"))
page = this.URItoListen + page;
//if start in IE
if (bIEBrowser)
{
//show in new Process: IE
string iexplore = Path.Combine(Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"),
"Internet Explorer"), "iexplore.exe");
System.Diagnostics.Process.Start(iexplore, page);
return;
}
//show in new process: DefaultBrowser
System.Diagnostics.Process.Start(page);
return;
}
#endregion
#region Console Membres
/// <summary>
/// Internal timer to update the console from Processor.
/// </summary>
private System.Windows.Forms.Timer _timerUpdateConsoleProcessor;
/// <summary>
/// Update the console from the Processor.
/// </summary>
private void timerUpdateConsoleProcessor_Tick(object sender, EventArgs e)
{
if (!_isStarted) return;
this.UpdateConsoleFromProcessor();
}
/// <summary>
/// Update the console from the Processor.
/// </summary>
private void UpdateConsoleFromProcessor()
{
if (_isDisposed) return;
//null value
if (_rtfConsole == null) return;
//check if invoke is required
if (_rtfConsole.InvokeRequired)
{
//not same thread: invoke
dlgUpdateConsoleFromProcessor Delegate = new dlgUpdateConsoleFromProcessor(this.UpdateConsoleFromProcessor);
_rtfConsole.Invoke(Delegate);
}
//test if differents
else if (_rtfConsole.Text != _strConsoleBeforeConsoleProcessor + _HostProcessor_ASPNET.Console)
{
//same thread: affect
_rtfConsole.Text = _strConsoleBeforeConsoleProcessor + _HostProcessor_ASPNET.Console;
}
}
/// <summary>
/// Delegate to synchronous threads on function UpdateConsoleFromProcessor().
/// </summary>
private delegate void dlgUpdateConsoleFromProcessor();
/// <summary>
/// Define the RichTextBox console of the server.
/// </summary>
public RichTextBox ConsoleRtf
{
get { return _rtfConsole; }
set
{
_rtfConsole = value;
}
}
private RichTextBox _rtfConsole = null;
/// <summary>
/// This string contains the console state before the consoleProcessor.
/// </summary>
private string _strConsoleBeforeConsoleProcessor = "";
/// <summary>
/// fctCleanConsole: ThreadSafe: Clean the text of the rtfConsole control.
/// </summary>
public void ConsoleClean()
{
if (_isDisposed) return;
//null value
if (_rtfConsole == null) return;
//check if invoke is required
if (_rtfConsole.InvokeRequired)
{
//not same thread: invoke
dlgConsoleClean Delegate = new dlgConsoleClean(this.ConsoleClean);
_rtfConsole.Invoke(Delegate);
}
else
{
//same thread: affect
_rtfConsole.Text = "";
}
}
/// <summary>
/// Delegate to synchronous threads on function ConsoleClean().
/// </summary>
private delegate void dlgConsoleClean();
/// <summary>
/// fctWriteConsole: ThreadSafe: Write given text to rtfConsole control.
/// </summary>
/// <param name="text">Text line to write in console control.</param>
public void ConsoleWrite(string text)
{
if (_isDisposed) return;
//null value
if (_rtfConsole == null) return;
if (text == null) text = "";
//show in rtfConsole
if (_rtfConsole.InvokeRequired)
{
//not same thread: invoke
dlgConsoleWrite Delegate = new dlgConsoleWrite(this.ConsoleWrite);
_rtfConsole.Invoke(Delegate, new object[] { text });
}
else
{
//new line char
if (!text.EndsWith("\n"))
text += "\n";
//same thread: affect
_rtfConsole.Text += text;
}
}
/// <summary>
/// Delegate to synchronous threads on function ConsoleWrite().
/// </summary>
private delegate void dlgConsoleWrite(string text);
#endregion
#region IDisposable Membres
/// <summary>
/// True if this is disposed or closed.
/// </summary>
private bool _isDisposed = false;
/// <summary>
/// Idem as Dispose().
/// </summary>
public void Close()
{
this.Dispose();
}
/// <summary>
/// Close the http listening properly and abort the thread.
/// </summary>
public void Dispose()
{
//check if already disposed
if (_isDisposed) return;
//dispose with managed ressources
this.Dispose(true);
//GC dont call finalizer of this
GC.SuppressFinalize(this);
}
/// <summary>
/// Finalizer: dispose but managed ressources.
/// </summary>
~ServerASPNET()
{
this.Dispose(false);
}
/// <summary>
/// Internal: Dispose.
/// </summary>
/// <param name="isDisposeManagedRessources"></param>
protected virtual void Dispose(bool isDisposeManagedRessources)
{
//check if already disposed
if (_isDisposed) return;
//set disposed
_isDisposed = true;
//dispose unmanaged ressources
this.StopThread_ServerASPNET();
if (isDisposeManagedRessources)
{
//dispose managed ressources
_HostProcessor_ASPNET = null;
_Thread_ServerASPNET = null;
_rtfConsole = null;
}
}
#endregion
}//class
/// <summary>
/// This class treat each http request from browser in function of given URI to listen.
/// </summary>
public class HostProcessorASPNET : MarshalByRefObject, IDisposable
{
#region Properties
/// <summary>
/// Return true if the processor is started and listening http requests.
/// </summary>
public bool IsStarted
{
get { return _IsStarted; }
}
private bool _IsStarted = false;
/// <summary>
/// Event on IsStarted changed.
/// BUG: cannot subscribe bacause different 'Application Domain'.
/// </summary>
public event IsStartedChangedEventHandler OnIsStartedChanged;
public delegate void IsStartedChangedEventHandler(object sender, EventArgs args);
/// <summary>
/// Return the URI to listen http requests.
/// Format : http://IptoListen:PortToListen/WebAppName/.
/// </summary>
public string URItoListen
{
get { return _URItoListen; }
//set ?
}
private string _URItoListen = null;
/// <summary>
/// Memorize the WebApp name.
/// </summary>
private string _WebAppName = null;
/// <summary>
/// Internal path to the WebApp directory.
/// </summary>
private string _PathWebApp = null;
/// <summary>
/// Internal HttpListener.
/// </summary>
private HttpListener _HttpListener = null;
/// <summary>
/// Internal Regex to find no ASP files.
/// </summary>
private Regex _rgxNoAspFile;
#endregion
#region Functions
/// <summary>
/// Stop the HttpListener properly. Set to null.
/// </summary>
public void StopHttpListening()
{
//check if closed
if (_isDisposed) return;
//check if started
if (!_IsStarted) return;
//stop HttpListener
_HttpListener.Stop();
_HttpListener.Abort();
_HttpListener = null;
//isStarted
_IsStarted = true;
this.ConsoleWrite("");
this.ConsoleWrite("Stop: HttpListening aborted !");
}
/// <summary>
/// Start the http listening on the given uriToListen.
/// </summary>
/// <param name="uriToListen">The URI to listen : http://IptoListen:PortToListen/WebAppName/.</param>
public void StartHttpListening(string uriToListen, string pathWebApp)
{
//check if closed
if (_isDisposed) return;
this.ConsoleClean();
//check if started
if (_IsStarted)
{
this.ConsoleWrite("Error: HostProcessorASPNET already started.");
return;
}
//check WebApp path
if (!new DirectoryInfo(pathWebApp).Exists)
{
this.ConsoleWrite("Error: Cannot find the WebApp path : '" + pathWebApp + "'.");
return;
}
//affect
_PathWebApp = pathWebApp;
//build _rgxNoAspFile //TODO
_rgxNoAspFile = new Regex(@"\.(gif|jpe?g?|png|tiff?|css|html?)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
//check uriToListen
if (uriToListen == null || !uriToListen.StartsWith("http://"))
{
this.ConsoleWrite("Error: URI to listen dont start with 'http://'.");
return;
}
_URItoListen = uriToListen;
//get _WebAppName
_WebAppName = new Regex(@"^https?://[^:/]*:?[^/]*/(?'WebAppName'.*?)/$")
.Match(_URItoListen).Groups["WebAppName"].Value;
//create new HttpListener
_HttpListener = new HttpListener();
//add uri to listen.
_HttpListener.Prefixes.Add(_URItoListen);
try
{
//start the listener
_HttpListener.Start();
}
catch (HttpListenerException err)
{
//TODO find exception on Vista
if (err.ErrorCode == 404) //HACK find exception on Vista
//error on windows vista
this.ConsoleWrite("Error Cause Windows Vista : Cant start the 'HttpListener' class.");
else
//error on windows xp
this.ConsoleWrite("Error: Cant start the 'HttpListener' class.");
this.ConsoleWrite("WinErr: " + err.ToString());
return;
}
//show started
this.ConsoleWrite("OK: 'HttpListener' Started and now listen http requests.");
_IsStarted = true;
if (this.OnIsStartedChanged != null)
this.OnIsStartedChanged(this, new EventArgs());
//start the global loop to treat each request from browser
this.RunHttpListening();
//never reach
}
/// <summary>
/// Internal: global loop to treat each request from browser.
/// </summary>
private void RunHttpListening()
{
//start the global loop to treat each request from browser
while (true) //to quit: _HttpListener.Abort()
{
//get httpContext from _HttpListener
HttpListenerContext httpContext;
try
{
//wait for http request from browser : lost constrol
this.ConsoleWrite("");
this.ConsoleWrite(" 'HttpListener' wait for http request from browser ...");
Application.DoEvents();
httpContext = _HttpListener.GetContext(); //to quit: _HttpListener.Abort()
Application.DoEvents();
}
catch (HttpListenerException err)
{
//if aborted
this.ConsoleWrite(" 'HttpListener' waiting aborted !");
this.ConsoleWrite(" Err: " + err.ToString());
return;
}
//get name of the page to show without '/WebAppName/'
string page = httpContext.Request.Url.LocalPath.Substring(1);
//delete '/WebAppName/'
if (page.StartsWith(_WebAppName))
page = page.Substring(_WebAppName.Length + 1);
//set default page if null
if (page.Length == 0)
page = "default.aspx";
//get query of the request
string query = httpContext.Request.Url.Query;
//add POSTed data in the body of request
if (httpContext.Request.HasEntityBody)
{
//read the content of the 'HTML form inputs' as a string
Stream streamFormInputs = httpContext.Request.InputStream;
StreamReader readerFormInputs = new StreamReader(streamFormInputs, httpContext.Request.ContentEncoding);
string sFormInputs = readerFormInputs.ReadToEnd();
readerFormInputs.Close();
streamFormInputs.Close();
//add to query
query += (query.Length != 0 ? "&" : "") + sFormInputs;
}
//show in console
string text = "/" + _WebAppName + "/" + page + (query.Length != 0 ? "?" + query : "");
this.ConsoleWrite(" Http Request received : '" + text + "'");
//get file relativ path
string file = page.Replace('/', '\\');
//treat App_GlobalResources
if (file.Contains("App_GlobalResources"))
file = file.Substring(file.IndexOf("App_GlobalResources"));
//treat no ASP files
if (_rgxNoAspFile.IsMatch(file))
{
//get full path file
string pathFile = Path.Combine(_PathWebApp, file);
//check if exist
if (!new FileInfo(pathFile).Exists)
{
//if no exist : close response
httpContext.Response.Close();
continue;
}
//open a FileStream
FileStream streamFile;
try
{
streamFile = new FileStream(pathFile, FileMode.Open);
}
catch (Exception err)
{
this.ConsoleWrite("Error: Opening FileStream to file : '" + pathFile + "'.");
continue;
}
//get streamResponse
Stream streamResponse = httpContext.Response.OutputStream;
//make new buffer
int bufferLength = 512;
Byte[] buffer = new Byte[bufferLength];
//read the file
int bytesRead = streamFile.Read(buffer, 0, bufferLength);
while (bytesRead > 0)
{
//write in the response
streamResponse.Write(buffer, 0, bytesRead);
//read the file
bytesRead = streamFile.Read(buffer, 0, bufferLength);
}
//close the streams
streamFile.Close();
httpContext.Response.OutputStream.Flush();
httpContext.Response.Close();
continue;
}
//new writer unicode
StreamWriter writerResponse = new StreamWriter(httpContext.Response.OutputStream,
System.Text.Encoding.Unicode);
//call ProcessRequest
this.ProcessRequest(file, query, writerResponse);
//close the response
try
{
writerResponse.Flush();
writerResponse.Close();
httpContext.Response.Close();
}
catch (Exception err)
{
}
}//while
//never reach
}
/// <summary>
/// Process to the full ASPNET response from the given fields.
/// </summary>
/// <param name="fileName">The name of the file to process.</param>
/// <param name="query">The full query of the http request.</param>
/// <param name="writerResponse">The writer of the response.</param>
public void ProcessRequest(string fileName, string query, TextWriter writerResponse)
{
//check if not closed
if (_isDisposed) return;
//check if started
if (!_IsStarted) return;
//create new aspnetWorker
SimpleWorkerRequest aspnetWorker = new SimpleWorkerRequest(fileName, query, writerResponse);
//process to the full ASPNET response
HttpRuntime.ProcessRequest(aspnetWorker);
//TODO chercher des fonctionnalités avec F1
//peut-être est-il possible d'implementer une fonctionnalité de redirection ?
//if (aspnetWorker.IsClientConnected)
//{
// string mapPath = aspnetWorker.MapPath();
// aspnetWorker.
//}
//send the response to client
aspnetWorker.FlushResponse(true);
}
#endregion
#region Console
/// <summary>
/// Return the text of the console.
/// </summary>
public string Console
{
get { return _strConsole; }
}
private string _strConsole = null;
/// <summary>
/// Event on Console text changed.
/// BUG: because different 'Application Domain'.
/// </summary>
public event ConsoleChangedEventHandler OnConsoleChanged;
public delegate void ConsoleChangedEventHandler(object sender, ConsoleChangedEventArgs args);
/// <summary>
/// Clean the text of the console.
/// Call by a new HttpListening.
/// </summary>
private void ConsoleClean()
{
//affect
_strConsole = "";
if (this.OnConsoleChanged != null)
this.OnConsoleChanged(this, new ConsoleChangedEventArgs(null));
}
/// <summary>
/// Write given line to the console.
/// </summary>
/// <param name="text">Text line to write in console control.</param>
private void ConsoleWrite(string text)
{
//null value
if (text == null) text = "";
//new line char
if (!text.EndsWith("\n"))
text += "\n";
//affect
_strConsole += text;
if (this.OnConsoleChanged != null)
this.OnConsoleChanged(this, new ConsoleChangedEventArgs(text));
}
#endregion
#region IDisposable Membres
/// <summary>
/// True if this is disposed/closed, and cannot function.
/// </summary>
private bool _isDisposed = false;
/// <summary>
/// Idem that Dispose();
/// </summary>
public void Close()
{
this.Dispose();
}
/// <summary>
/// Stop the _HttpListener properly. _HttpListener = null;
/// </summary>
public void Dispose()
{
//check if already disposed
if (_isDisposed) return;
//dispose with managed ressources
this.Dispose(true);
//Garbage Collector dont call finalizer of this
GC.SuppressFinalize(this);
}
/// <summary>
/// Finalizer: dispose but managed ressources.
/// </summary>
~HostProcessorASPNET()
{
this.Dispose(false);
}
/// <summary>
/// Internal: Stop the HttpListener properly.
/// </summary>
/// <param name="isDisposeManagedRessources">Internal: true to annul HttpListener.</param>
protected virtual void Dispose(bool isDisposeManagedRessources)
{
//check if already disposed
if (_isDisposed) return;
//set disposed
_isDisposed = true;
//dispose unmanaged ressources
this.StopHttpListening();
if (isDisposeManagedRessources)
{
//dispose managed ressources
_HttpListener = null;
}
}
#endregion
}//class HostProcessorASPNET
/// <summary>
/// The EventArg for the event OnConsoleChanged.
/// </summary>
public class ConsoleChangedEventArgs : EventArgs
{
/// <summary>
/// The new line added to the console.
/// Is null if console is cleaned.
/// </summary>
public string NewLineText
{
get { return _NewLineText; }
}
private string _NewLineText = null;
/// <summary>
/// Ctor, init the EventArg with given newLineText.
/// </summary>
/// <param name="newLineText">The new line added to the console.</param>
public ConsoleChangedEventArgs(string newLineText)
{
//affect
_NewLineText = newLineText;
}
}//class ConsoleChangedEventArgs
}//namespace
Conclusion :
Vous pouvez faire un copier/coller d'une de vos WebApp ASPNET 2.0 dans cet exemple et tester si ca fonctionne.
Normalement tout fonctionne sauf les redirections en fin de traitement PostBack (auquel je m'attaquerais surement dans les semaines à venir...).
Faites moi un commentaire si vous détecter un BUG.
Si quelqu'un réussit à améliorer le code qu'il n'hésite pas à m'en faire part ou à poster sa nouvelle source sur le site.
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.