HTTP-Statuscode einer Webseite mit C# ermitteln
Hin und wieder kann es – beispielsweise im Rahmen eines Monitorings -
erforderlich sein, den HTTP-Statuscode einer Webseite zu ermitteln. Das lässt sich mit C# grundsätzlich bereits mit wenigen Zeilen lösen:
Hilfsmethode
In der Praxis möchte man vermutlich zumindest noch das Weiterleitungs-
und Zwischenspeicherverhalten konfigurieren, daher bietet es sich an, unsere Methode entsprechend zu erweitern:
01020304050607
public static HttpStatusCode GetHttpStatusCode(Uri uri) { HttpWebRequest httpWebRequest = WebRequest.CreateHttp(uri);
using(HttpWebResponse webResponse = (HttpWebResponse) httpWebRequest.GetResponse()) { return webResponse.StatusCode; }}
Hilfsmethode (erweitert)
Die Verwendung gestaltet sich nun sehr intuitiv:
010203040506070809101112131415
public static HttpStatusCode GetHttpStatusCode( Uri uri, bool allowAutoRedirect = false, RequestCacheLevel requestCacheLevel = RequestCacheLevel.NoCacheNoStore) { HttpWebRequest httpWebRequest = WebRequest.CreateHttp(uri); { httpWebRequest.AllowAutoRedirect = allowAutoRedirect; httpWebRequest.CachePolicy = new RequestCachePolicy(requestCacheLevel); }
using(HttpWebResponse webResponse = (HttpWebResponse) httpWebRequest.GetResponse()) { return webResponse.StatusCode; }}
Hilfsmethode verwenden
Wenn ihr den Wert der 01020304
Uri uri = new Uri("https://ccc.coders-online.net/");
HttpStatusCode statusCode = GetHttpStatusCode(uri); // Found (302)statusCode = GetHttpStatusCode(uri, true); // OK (200)
HttpStatusCode
-Aufzählung zu int
konvertiert erhaltet ihr im Übrigen den entsprechenden numerischen Wert nach Definition des HTTP-Protokolls. RandomNumberGenerator-Implementierung in C# für random.org
In einem früheren Beitrag zum Thema (Pseudo-)Zufallszahlen in C# hatte ich bereits die Klassen
Unsere Klasse soll die Werte jedoch nicht selbst erzeugen, sondern vom Anbieter random.org über dessen Schnittstelle auf Basis von JSON-RPC beziehen. Der Dienst ist – zumindest derzeit und in gewissen Grenzen – kostenlos, wobei ein API-Schlüssel benötigt wird, den ihr dem Konstruktor der Klasse übergeben müsst.
Hinweis: Ich möchte vor der eigentlichen Implementierung noch darauf hinweisen, dass dieser Beitrag weder die Güte der darüber ermittelten (Pseudo-)Zufallszahlen noch die Vertrauenswürdigkeit des Anbieters bewerten soll. Dieser Beitrag soll lediglich eine sehr einfache und beispielhafte Implementierung für den Fall aufzeigen, dass ihr euch eben für diese Nutzung (beispielsweise für weniger kritische Daten oder aus reinem Interesse) entschieden habt.
Random
und RNGCryptoServiceProvider
vorgestellt. Letztere leitet dabei von der abstrakten Klasse RandomNumberGenerator
ab, welche wir im Folgenden ebenfalls als Basis für eine Klasse verwenden werden.Unsere Klasse soll die Werte jedoch nicht selbst erzeugen, sondern vom Anbieter random.org über dessen Schnittstelle auf Basis von JSON-RPC beziehen. Der Dienst ist – zumindest derzeit und in gewissen Grenzen – kostenlos, wobei ein API-Schlüssel benötigt wird, den ihr dem Konstruktor der Klasse übergeben müsst.
Hinweis: Ich möchte vor der eigentlichen Implementierung noch darauf hinweisen, dass dieser Beitrag weder die Güte der darüber ermittelten (Pseudo-)Zufallszahlen noch die Vertrauenswürdigkeit des Anbieters bewerten soll. Dieser Beitrag soll lediglich eine sehr einfache und beispielhafte Implementierung für den Fall aufzeigen, dass ihr euch eben für diese Nutzung (beispielsweise für weniger kritische Daten oder aus reinem Interesse) entschieden habt.
Klasse implementieren
Die eigentliche Verwendung gestaltet sich nun kompatibel zur Basisklasse 01020304050607080910111213141516171819202122232425262728293031323334353637383940414243
public class RandomOrg : RandomNumberGenerator { private const string ApiMethod = "generateIntegers"; private const string ApiUrl = "https://api.random.org/json-rpc/1/invoke"; private const string ApiVersion = "2.0";
private readonly string apiKey;
public RandomOrg(string apiKey) { this.apiKey = apiKey; }
public override void GetBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length), data, data.Length); }
public override void GetNonZeroBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length, 1), data, data.Length); }
private byte[] SendRequest(int n, byte min = 0) { using(WebClient webClient = new WebClient()) { JavaScriptSerializer js = new JavaScriptSerializer();
dynamic data = js.Deserialize<dynamic>(webClient.UploadString(ApiUrl, js.Serialize(new { jsonrpc = ApiVersion, method = ApiMethod, @params = new { apiKey = this.apiKey, n = n, min = min, max = 255 }, id = DateTime.Now.Ticks })));
if(data.ContainsKey("error")) { throw new Exception(data["error"]["message"]); }
return Array.ConvertAll((object[]) data["result"]["random"]["data"], Convert.ToByte); } }}
RandomNumberGenerator
und entsprechend einfach. Um beispielsweise ein byte
-Feld mit fünf Zahlen zu befüllen, kann folgender Aufruf dienen:
Parameter einer Java-Methode per Reflexion ermitteln
Für ein kleines Projekt (mehr dazu in einem separaten Beitrag) war es notwendig, die Parameter von Methoden per Reflexion in Java zu ermitteln. Die entsprechende Funktionalität findet sich im Paket
Wir bereits früher angekündigt werde ich zur Reflexion in C# noch einen etwas ausführlicheren Beitrag schreiben und dann dort eine entsprechende Implementierung ähnlicher Funktionalität vorstellen.
java.lang.reflect
.
Parameter einer Java-Methode per Reflexion ermitteln
Ähnlich zu LINQ bei C# gibt es für Java sogenannte Streams; wir können obige Implementierung damit beispielsweise wie folgt unter einem funktionalem Paradigma schreiben:
0102030405060708091011121314151617181920
public static List<Map<String,String>> GetMethodParameterList( String typeName, String methodName) throws ClassNotFoundException { List<Map<String,String>> methodList = new ArrayList<>();
for(Method method : Class.forName(typeName).getMethods()) { if(method.getName().equals(methodName)) { HashMap<String,String> parameterList = new HashMap<>();
for(Parameter parameter : method.getParameters()) { parameterList.put(parameter.getName(), parameter.getType().getCanonicalName()); }
methodList.add(parameterList); } }
return methodList;}
Implementierung mit Java-Streams
Die Verwendung gestaltet sich jeweils identisch:
010203040506070809101112
public static List<Map<String,String>> GetMethodParameterList( String typeName, String methodName) throws ClassNotFoundException { return Stream.of(Class.forName(typeName).getMethods()) .filter(m -> m.getName().equals(methodName)) .map(m -> Stream.of(m.getParameters()).collect(Collectors.toMap( k -> k.getName(), v -> v.getType().getCanonicalName() ))) .collect(Collectors.toList());}
Methode verwenden und Ergebnis auf der Standardausgabe ausgeben
Es gilt schließlich noch eine kleine Besonderheit zu berücksichtigen: Standardmäßig stehen die Bezeichner der Parameter unter Java nicht zur Verfügung, sondern würden schlicht durchnummeriert. Um die tatsächlichen Bezeichner zu erhalten, muss mit der Option 01020304050607
List<Map<String,String>> methodList = GetMethodParameterList(typeName, methodName);
for(Map<String,String> parameterList : methodList) { for(Map.Entry<String,String> parameter : parameterList.entrySet()) { System.out.println(parameter.getValue() + " " + parameter.getKey()); }}
-parameters
kompiliert werden. Verbreitete Entwicklungsumgebungen wie Eclipse oder NetBeans bieten hierfür entsprechende Einstellungen.Wir bereits früher angekündigt werde ich zur Reflexion in C# noch einen etwas ausführlicheren Beitrag schreiben und dann dort eine entsprechende Implementierung ähnlicher Funktionalität vorstellen.
Hintergrundaufgaben unter ASP.NET ausführen
Im Vergleich zu manch anderen Laufzeitumgebungen für Web-Anwendungen ist es bei ASP.NET auch hier recht einfach, Aufgaben im Hintergrund (beispielsweise über separate Threads) auszuführen. Obwohl das grundsätzlich jederzeit sehr einfach – abgesehen von den inhärenten Schwierigkeiten, die Nebenläufigkeit mit sich bringen kann – möglich ist, ist das einfache Vorgehen, wie man es aus dem Desktop-Bereich kennt, fehleranfällig.
Im einfachsten Fall würde man asynchron per
Bereits seit längerer Zeit werden daher u. a. die beiden Methoden
Im einfachsten Fall würde man asynchron per
Task.Run(…)
(als mit häufig genutzten Standardwerten vorbelegte Abkürzung für Task.Factory.StartNew(…)
) zur Verwendung des Standard-Thread-Pools oder manuell per Thread
-Klasse arbeiten. Dies ist jedoch insofern problematisch, als dass die ASP.NET-Anwendungsdomäne davon nicht benachrichtigt wird und daher beispielsweise einerseits auf die Beendigung des Vorgangs nicht gewartet und andererseits auf das Herunterfahren des Arbeitsprozesses nicht reagiert werden kann.Bereits seit längerer Zeit werden daher u. a. die beiden Methoden
RegisterObject
und UnregisterObject
der HostingEnvironment
-Klasse zur Verfügung gestellt, deren Verwendung jedoch etwas umständlich ist und auf Grund neuerer Möglichkeiten, die gleich im Anschluss vorgestellt werden, an dieser Stelle nicht weiter ausgeführt werden soll. Wir verwenden stattdessen die seit .NET 4.5.2 verfügbare QueueBackgroundWorkItem
-Methode:
Hintergrundaufgabe hinzufügen
Etwas ungeschickt ist es jedoch, wenn dies beispielsweise in einem separaten Projekt ausgelagert werden soll, um jeweils von einer Desktop- und Web-Anwendung darauf zuzugreifen – außerhalb der ASP.NET-Laufzeitumgebung ist die Verwendung dieser Methode nämlich nicht möglich. Daher stellen wir für diesen Fall eine Alternative zur Verfügung, um zumindest ähnliches Verhalten für alle Anwendungsfälle zur Verfügung zu stellen:
010203
HostingEnvironment.QueueBackgroundWorkItem(cT => { /* Implementierung der Aufgabe */});
Hilfsmethode (inkl. Fallback) implementieren
010203040506070809101112
public static class TaskUtility { public static void DoBackgroundWork( Action<CancellationToken> action, CancellationToken cancellationToken = default(CancellationToken) ) { if(HostingEnvironment.IsHosted) { HostingEnvironment.QueueBackgroundWorkItem(action); } else { Task.Run(() => action(cancellationToken), cancellationToken); } }}
Hilfsmethode verwenden
Alternativ wäre es natürlich auch möglich, nur die eigentliche Aufgabe auszulagern und dann anwendungsspezifisch zu starten. Weiterführende Informationen zur Verwendung des CancellationToken stellt das MSDN insbesondere über die Struktur 010203
TaskUtility.DoBackgroundWork(cT => { /* Implementierung der Aufgabe */});
CancellationToken
und die Klasse CancellationTokenSource
zur Verfügung. ZIP-Archive mit C# (ent-)packen
In einem früheren Beitrag zur (De-)Kompression mit C# hatte ich bereits einen Beitrag zu ZIP-Archiven in Aussicht gestellt. Diese sind praktisch, um Dateien und Verzeichnisstrukturen komprimiert und zusammenhängend speichern und austauschen zu können und werden außerdem auf praktisch jeder Plattform unterstützt. Daher möchte ich mit diesem Beitrag zeigen, wie sich mit C# entsprechende Archive automatisch erstellen lassen.
Das .NET-Framework liefert im Namensraum
Zuerst möchte ich ein paar Möglichkeiten aufzeigen, um ZIP-Archive zu erstellen:
Das .NET-Framework liefert im Namensraum
System.IO.Compression
grundlegende Funktionalität zur Arbeit mit entsprechenden Archiven, auf die wir in diesem Beitrag setzen werden. Darüber hinaus gibt es jedoch eine Vielzahl weiterer Implementierungen (z. B. DotNetZip oder SharpZipLib), welche beispielsweise die Erstellung von passwortgeschützten Archiven ermöglichen und weitere Konfigurationsmöglichkeiten bieten.Zuerst möchte ich ein paar Möglichkeiten aufzeigen, um ZIP-Archive zu erstellen:
Archiv aus bestehendem Verzeichnis erstellen
01
ZipFile.CreateFromDirectory("data", "data.zip");
Archiv aus bestehenden Dateien erstellen
010203040506
using(FileStream fileStream = File.OpenWrite("data.zip")) { using(ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create)) { zipArchive.CreateEntryFromFile("data/a.txt", "a.txt"); zipArchive.CreateEntryFromFile("data/b.txt", "b.txt"); }}
Archiv manuell zusammenstellen
Abschließend möchten wir auf bestehende Archive zugreifen:
010203040506070809
using(FileStream fileStream = File.OpenWrite("data.zip")) { using(ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create)) { ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry("README");
using(StreamWriter streamWriter = new StreamWriter(zipArchiveEntry.Open())) { streamWriter.Write("Hallo Welt!"); } }}
Archiv vollständig entpacken
01
ZipFile.ExtractToDirectory("data.zip", "data");
Archiv einlesen und verarbeiten
Sehr viel mehr Möglichkeiten stehen leider tatsächlich nicht zur Verfügung; für weitergehende Einstellungen gibt es jedoch beispielsweise die bereits erwähnten externen Bibliotheken. Durch die Verwendung von Datenströmen besteht aber grundsätzlich die Möglichkeit, nahezu beliebige Quellen (und Ziele) für Archiv-Einträge heranzuziehen. 01020304050607080910111213141516
using(FileStream fileStream = File.OpenRead("data.zip")) { using(ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read)) { foreach(ZipArchiveEntry zipArchiveEntry in zipArchive.Entries) { // "README" separat berücksichtigen if(zipArchiveEntry.Name == "README") { using(StreamReader streamReader = new StreamReader(zipArchiveEntry.Open())) { // Inhalt einlesen und auf Konsole ausgeben Console.WriteLine(streamReader.ReadToEnd()); } } else { // Eintrag als Datei speichern zipArchiveEntry.ExtractToFile(zipArchiveEntry.Name); } } }}
Project links
-
BitStadt – Stadtportal
Berlin · Hamburg · Amsterdam -
CCC – Fahrplan
Schedules for the CCCongress
Categories / Archive | Übersicht
- PHP functions in C# (136)
- Dictionary (257)
Tags
.NET · ADO.NET · Work · ASP.NET MVC · Blog · C# · Generics · Society · Computer Science · Java · LINQ · Logic · Mathematics · Network · PHP · Project · Security · Software development · Studies · Technics · Theory · Web design · WPF