Bereitstellungsbeispiel in C#
von Walter Oliver
Übersicht
Im Laufe der Jahre ist es für erweiterte Webbenutzer einfach geworden, ihre eigene Website zu erstellen. Sobald ein Endnutzer seinen Domänennamen registriert hat, kann er zwischen verschiedenen Arten von Hostern wählen. Webhoster sind weltweit verfügbar, um die Anforderungen von Endbenutzern zu erfüllen. Unabhängig davon, welcher Hoster ausgewählt wird, ist das Szenario der Registrierung und Erstellung einer Website in allen Fällen ähnlich.
Zunächst muss ein neues Benutzerkonto für den Benutzer eingerichtet werden. Nachdem das Konto eingerichtet wurde, entscheidet der Endbenutzer, welche Features und Optionen die Webseite enthalten soll: z. B. wie viel Speicherplatz, FTP-Funktion, Erstellung virtueller Verzeichnisse, ob Datenbanken erforderlich sind usw. Hoster erstellen Systemsteuerungen oder Dashboardanwendungen, mit denen Endbenutzer diese Features erstellen und verwalten können.
Es gibt eine Reihe von Möglichkeiten, wie diese Funktionen in die Systemsteuerung implementiert werden können. In diesem Abschnitt befassen wir uns mit der Implementierung des Bereitstellungsaspekts dieser Features über verwalteten Code. Eine Gliederung der Features lautet wie folgt:
Bereitstellen eines neuen Benutzerkontos
Der Sitebesitzer, der die Webseite verwaltet und verwaltet, benötigt ein neues Benutzerkonto. Das Konto kann entweder ein Active Directory-Konto® oder ein lokales Benutzerkonto sein. Die folgenden Codefragmente veranschaulichen die Erstellung eines lokalen Kontos. Beachten Sie, dass der System.DirectoryServices-Namespace angegeben werden muss.
public static bool CreateLocalUserAccount(string userName, string password)
{
try
{
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName", "Invalid User Name.");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password", "Invalid Password.");
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" +
Environment.MachineName + ",computer");
bool userFound = false;
try
{
if (directoryEntry.Children.Find(userName, "user") != null)
userFound = true;
}
catch
{
userFound = false;
}
if (!userFound)
{
DirectoryEntry newUser = directoryEntry.Children.Add(userName, "user");
newUser.Invoke("SetPassword", new object[] { password });
newUser.Invoke("Put", new object[] { "Description", "Application Pool User Account" });
newUser.CommitChanges();
newUser.Close();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Inhaltsspeicher erstellen
Für eine Website ist ein Speicherort auf den Dateisystemen erforderlich, an den Benutzer Inhalte für die Webseite hochladen können. Die Microsoft® .NET Directory-Klasse stellt die Anwendungsprogrammierschnittstelle (API) bereit, um Verzeichnisse im Dateisystem zu erstellen.
Directory.CreateDirectory(parentPath + "\\" + directoryName);
Der Inhaltsspeicher erfordert bestimmte Berechtigungen, die so konfiguriert sind, dass Benutzer ihre eigenen Inhalte verwalten können. Das folgende Codefragment veranschaulicht, wie Verzeichnisberechtigungen mithilfe von verwaltetem Code in C# festgelegt werden:
public static bool AddDirectorySecurity(string directoryPath, string userAccount, FileSystemRights rights, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType controlType)
{
try
{
// Create a new DirectoryInfo object.
DirectoryInfo dInfo = new DirectoryInfo(directoryPath);
// Get a DirectorySecurity object that represents the
// current security settings.
DirectorySecurity dSecurity = dInfo.GetAccessControl();
// Add the FileSystemAccessRule to the security settings.
dSecurity.AddAccessRule(new FileSystemAccessRule(userAccount, rights, inheritanceFlags, propagationFlags, controlType));
// Set the new access settings.
dInfo.SetAccessControl(dSecurity);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Das folgende Codefragment veranschaulicht, wie Sie das Datenträgerkontingent mithilfe von verwaltetem Code festlegen, wenn das Datenträgerkontingent eingeschränkt ist. Um die Datenträgerkontingentverwaltung zu verwenden, müssen Sie einen Verweis auf die Windows-Kontingentverwaltungskomponente® hinzufügen. Sie befindet sich unter Windows\system32\dskquota.dll.
public static bool AddUserDiskQuota(string userName, double quota, double quotaThreshold, string diskVolume)
{
try
{
DiskQuotaControlClass diskQuotaCtrl = GetDiskQuotaControl(diskVolume);
diskQuotaCtrl.UserNameResolution = UserNameResolutionConstants.dqResolveNone;
DIDiskQuotaUser diskUser = diskQuotaCtrl.AddUser(userName);
diskUser.QuotaLimit = quota;
diskUser.QuotaThreshold = quotaThreshold;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
So erstellen Sie einen Anwendungspool
Ein Anwendungspool definiert die Einstellungen für einen Arbeitsprozess, der eine oder mehrere Internetinformationsdienste 7 (IIS 7)-Anwendungen hostet, die ihre Anforderungsverarbeitung ausführen. Der Anwendungspool ist eine Einheit der Prozessisolation, da alle Anforderungsverarbeitung für eine Anwendung innerhalb der Arbeitsprozesse des Anwendungspools ausgeführt wird.
Ein Anwendungspool stellt auch eine Isolation bereit, die wiederum Sicherheit bietet. Jeder Anwendungspool kann mit einer eindeutigen Identität ausgeführt werden und kann eine Zugriffssteuerungsliste (Access Control List, ACL) verwenden, um zu verhindern, dass Anwendungen in anderen Pools auf ihre Ressourcen zugreifen. Das folgende Codefragment veranschaulicht die Erstellung eines Anwendungspools, das Festlegen der Identität und das Festlegen von Eigenschaften.
public static bool CreateApplicationPool(string applicationPoolName, ProcessModelIdentityType identityType, string applicationPoolIdentity, string password, string managedRuntimeVersion, bool autoStart, bool enable32BitAppOnWin64,ManagedPipelineMode managedPipelineMode, long queueLength, TimeSpan idleTimeout, long periodicRestartPrivateMemory, TimeSpan periodicRestartTime)
{
try
{
if (identityType == ProcessModelIdentityType.SpecificUser)
{
if (string.IsNullOrEmpty(applicationPoolName))
throw new ArgumentNullException("applicationPoolName", "CreateApplicationPool: applicationPoolName is null or empty.");
if (string.IsNullOrEmpty(applicationPoolIdentity))
throw new ArgumentNullException("applicationPoolIdentity", "CreateApplicationPool: applicationPoolIdentity is null or empty.");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password", "CreateApplicationPool: password is null or empty.");
}
using (ServerManager mgr = new ServerManager())
{
ApplicationPool newAppPool = mgr.ApplicationPools.Add(applicationPoolName);
if (identityType == ProcessModelIdentityType.SpecificUser)
{
newAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
newAppPool.ProcessModel.UserName = applicationPoolIdentity;
newAppPool.ProcessModel.Password = password;
}
else
{
newAppPool.ProcessModel.IdentityType = identityType;
}
if (!string.IsNullOrEmpty(managedRuntimeVersion))
newAppPool.ManagedRuntimeVersion = managedRuntimeVersion;
newAppPool.AutoStart = autoStart;
newAppPool.Enable32BitAppOnWin64 = enable32BitAppOnWin64;
newAppPool.ManagedPipelineMode = managedPipelineMode;
if (queueLength > 0)
newAppPool.QueueLength = queueLength;
if (idleTimeout != TimeSpan.MinValue)
newAppPool.ProcessModel.IdleTimeout = idleTimeout;
if (periodicRestartPrivateMemory > 0)
newAppPool.Recycling.PeriodicRestart.PrivateMemory = periodicRestartPrivateMemory;
if (periodicRestartTime != TimeSpan.MinValue)
newAppPool.Recycling.PeriodicRestart.Time = periodicRestartTime;
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Erstellen eines Standorts
Eine Site ist der logische Container der obersten Ebene, der angibt, wie HTTP-Anfragen empfangen und verarbeitet werden. Eine Site definiert eine Gruppe von Bindungen, die bestimmen, wie die Site auf eingehende Anforderungen lauscht, und eine Site enthält die Definitionen von Anwendungen oder virtuellen Verzeichnissen, die den URL-Namespace der Site für die Strukturierung des Anwendungsinhalts partitionieren.
Das folgende Codefragment veranschaulicht, wie eine neue Site erstellt wird. Die Namespaces Microsoft.Web.Administration sind erforderlich, wenn das ServerManager-Objekt implementiert wird. Beachten Sie, dass auf die Anwendungssammlung, Sitesammlung und Bindungsobjekte über das ServerManager-Objekt zugegriffen wird.
public static bool CreateWebSite(string siteName)
{
try
{
if (string.IsNullOrEmpty(siteName))
{
throw new ArgumentNullException("siteName", "CreateWebSite: siteName is null or empty.");
}
//get the server manager instance
using (ServerManager mgr = new ServerManager())
{
Site newSite = mgr.Sites.CreateElement();
//get site id
newSite.Id = GenerateNewSiteID(mgr, siteName);
newSite.SetAttributeValue("name", siteName);
mgr.Sites.Add(newSite);
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Erstellen Sie eine Bindung
Eine Bindung ist eine Kombination aus Protokollnamen und protokollspezifischen Bindungsinformationen. IIS 7 unterstützt zwar Multiprotokollbindungen (z. B. Windows® Communication Foundation [WCF] SOAP-TCP und FTP), der folgende Code verwendet nur den HTTP-Pfad; eine HTTP-Bindung definiert effektiv einen HTTP-Endpunkt, der eine bestimmte Schnittstellen-IP-Adresse (oder alle Schnittstellen), eine bestimmte Portnummer oder einen bestimmten HTTP-Hostheader (oder alle Hostheader) überwacht. Auf diese Weise können Sie viele Sites auf Ihrem Server konfigurieren, die über unterschiedliche IP-Adressen, verschiedene Ports oder auf derselben IP-Adresse oder demselben Port, aber mit unterschiedlichen Hostheadern überwachen.
public static bool AddSiteBinding(string siteName, string ipAddress, string tcpPort, string hostHeader, string protocol)
{
try
{
if (string.IsNullOrEmpty(siteName))
{
throw new ArgumentNullException("siteName", "AddSiteBinding: siteName is null or empty.");
}
//get the server manager instance
using (ServerManager mgr = new ServerManager())
{
SiteCollection sites = mgr.Sites;
Site site = mgr.Sites[siteName];
if (site != null)
{
string bind = ipAddress + ":" + tcpPort + ":" + hostHeader;
//check the binding exists or not
foreach (Binding b in site.Bindings)
{
if (b.Protocol == protocol && b.BindingInformation == bind)
{
throw new Exception("A binding with the same ip, port and host header already exists.");
}
}
Binding newBinding = site.Bindings.CreateElement();
newBinding.Protocol = protocol;
newBinding.BindingInformation = bind;
site.Bindings.Add(newBinding);
mgr.CommitChanges();
return true;
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
Erstellen einer Stammanwendung
Eine Anwendung ist ein logischer Container für die Websitefunktionalität, sodass Sie den Website-URL-Namespace in separate Teile unterteilen und das Laufzeitverhalten jedes Teils einzeln steuern können.
Sie können beispielsweise jede Anwendung so konfigurieren, dass sie sich in einem separaten Anwendungspool befindet. Dadurch wird eine Anwendung von anderen Anwendungen isoliert, indem sie in einen separaten Prozess eingegliedert wird und dieser Prozess optional mit einer anderen Windows-Identität ausgeführt wird, um eine Sandbox zu schaffen.
public static bool AddApplication(string siteName, string applicationPath, string applicationPool, string virtualDirectoryPath, string physicalPath, string userName, string password)
{
try
{
if (string.IsNullOrEmpty(siteName))
throw new ArgumentNullException("siteName", "AddApplication: siteName is null or empty.");
if (string.IsNullOrEmpty(applicationPath))
throw new ArgumentNullException("applicationPath", "AddApplication: application path is null or empty.");
if (string.IsNullOrEmpty(physicalPath))
throw new ArgumentNullException("PhysicalPath", "AddApplication: Invalid physical path.");
if (string.IsNullOrEmpty(applicationPool))
throw new ArgumentNullException("ApplicationPool", "AddApplication: application pool namespace is Nullable or empty.");
using (ServerManager mgr = new ServerManager())
{
ApplicationPool appPool = mgr.ApplicationPools[applicationPool];
if (appPool == null)
throw new Exception("Application Pool: " + applicationPool + " does not exist.");
Site site = mgr.Sites[siteName];
if (site != null)
{
Application app = site.Applications[applicationPath];
if (app != null)
throw new Exception("Application: " + applicationPath + " already exists.");
else
{
app = site.Applications.CreateElement();
app.Path = applicationPath;
app.ApplicationPoolName = applicationPool;
VirtualDirectory vDir = app.VirtualDirectories.CreateElement();
vDir.Path = virtualDirectoryPath;
vDir.PhysicalPath = physicalPath;
if (!string.IsNullOrEmpty(userName))
{
if (string.IsNullOrEmpty(password))
throw new Exception("Invalid Virtual Directory User Account Password.");
else
{
vDir.UserName = userName;
vDir.Password = password;
}
}
app.VirtualDirectories.Add(vDir);
}
site.Applications.Add(app);
mgr.CommitChanges();
return true;
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
Erstellen eines virtuellen Verzeichnisses
Ein virtuelles Verzeichnis ordnet einen Teil des Anwendungs-URL-Namespace einem physischen Speicherort auf dem Datenträger zu. Wenn eine Anfrage an die Anwendung weitergeleitet wird, wird derselbe Algorithmus verwendet, um das virtuelle Verzeichnis mit dem längsten virtuellen Pfad zu finden, der dem Rest des absoluten Pfads der Anfrage nach dem Anwendungspfad entspricht.
public static bool AddVirtualDirectory(string siteName, string application, string virtualDirectoryPath, string physicalPath, string userName, string password)
{
try
{
if (string.IsNullOrEmpty(siteName))
throw new ArgumentNullException("siteName", "AddVirtualDirectory: siteName is null or empty.");
if (string.IsNullOrEmpty(application))
throw new ArgumentNullException("application", "AddVirtualDirectory: application is null or empty.");
if (string.IsNullOrEmpty(virtualDirectoryPath))
throw new ArgumentNullException("virtualDirectoryPath", "AddVirtualDirectory: virtualDirectoryPath is null or empty.");
if (string.IsNullOrEmpty(physicalPath))
throw new ArgumentNullException("physicalPath", "AddVirtualDirectory: physicalPath is null or empty.");
using (ServerManager mgr = new ServerManager())
{
Site site = mgr.Sites[siteName];
if (site != null)
{
Application app = site.Applications[application];
if (app != null)
{
VirtualDirectory vDir = app.VirtualDirectories[virtualDirectoryPath];
if (vDir != null)
{
throw new Exception("Virtual Directory: " + virtualDirectoryPath + " already exists.");
}
else
{
vDir = app.VirtualDirectories.CreateElement();
vDir.Path = virtualDirectoryPath;
vDir.PhysicalPath = physicalPath;
if (!string.IsNullOrEmpty(userName))
{
if (string.IsNullOrEmpty(password))
throw new Exception("Invalid Virtual Directory User Account Password.");
else
{
vDir.UserName = userName;
vDir.Password = password;
}
}
app.VirtualDirectories.Add(vDir);
}
mgr.CommitChanges();
return true;
}
else
throw new Exception("Application: " + application + " does not exist.");
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
Erstellen einer FTP-Site
Eine FTP-Site ermöglicht Es Kunden, Inhalte auf ihre Website hochzuladen und bietet die Möglichkeit, Dateien über das Internet zu verschieben. Der Kunde kann sowohl Inhalte als auch Zugriff verwalten. Die Erstellung einer FTP-Site ähnelt der Erstellung einer Website: Ein Anwendungspool und eine Site werden mit einer Stammanwendung und einem virtuellen Verzeichnis erstellt, und dann wird eine FTP-Bindung angewendet.
public static bool CreateFtpSite(string applicationPoolName,string siteName, string domainName, string userName, string password,string contentPath, string ipAddress, string tcpPort, string hostHeader)
{
try
{
//provision the application pool
using (ServerManager mgr = new ServerManager())
{
ApplicationPool appPool = mgr.ApplicationPools[applicationPoolName];
//per IIS7 team recommendation, we always create a new application pool
//create new application pool
if (appPool == null)
{
appPool = mgr.ApplicationPools.Add(applicationPoolName);
//set the application pool attribute
appPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
appPool.ProcessModel.UserName = domainName + "\\" + userName;
appPool.ProcessModel.Password = password;
}
//if the appPool is null, we throw an exception. The appPool should be created or already exists.
if (appPool == null)
throw new Exception("Invalid Application Pool.");
//if the site already exists, throw an exception
if (mgr.Sites[siteName] != null)
throw new Exception("Site already exists.");
//create site
Site newSite = mgr.Sites.CreateElement();
newSite.Id = GenerateNewSiteID(mgr, siteName);
newSite.SetAttributeValue("name", siteName);
newSite.ServerAutoStart = true;
mgr.Sites.Add(newSite);
//create the default application for the site
Application newApp = newSite.Applications.CreateElement();
newApp.SetAttributeValue("path", "/"); //set to default root path
newApp.SetAttributeValue("applicationPool", applicationPoolName);
newSite.Applications.Add(newApp);
//create the default virtual directory
VirtualDirectory newVirtualDirectory = newApp.VirtualDirectories.CreateElement();
newVirtualDirectory.SetAttributeValue("path", "/");
newVirtualDirectory.SetAttributeValue("physicalPath", contentPath);
newApp.VirtualDirectories.Add(newVirtualDirectory);
//add the bindings
Binding binding = newSite.Bindings.CreateElement();
binding.SetAttributeValue("protocol", "ftp");
binding.SetAttributeValue("bindingInformation", ipAddress + ":" + tcpPort + ":" + hostHeader);
newSite.Bindings.Add(binding);
//commit the changes
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}