Suche: API

RandomNumberGenerator-Implementierung in C# für random.org 👍 👎

In einem früheren Beitrag zum Thema (Pseudo-)Zufallszahlen in C# hatte ich bereits die Klassen 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
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445
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); } }}
Die eigentliche Verwendung gestaltet sich nun kompatibel zur Basisklasse RandomNumberGenerator und entsprechend einfach. Um beispielsweise ein byte-Feld mit fünf Zahlen zu befüllen, kann folgender Aufruf dienen:
Klasse verwenden
01020304
using(RandomOrg numberGenerator = new RandomOrg("@ToDo")) {  // API-Schlüssel eintragen    byte[] randomNumbers = new byte[5];    numberGenerator.GetBytes(randomNumbers);}

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 java.lang.reflect.
Parameter einer Java-Methode per Reflexion ermitteln
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;}
Ä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:
Implementierung mit Java-Streams
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());}
Die Verwendung gestaltet sich jeweils identisch:
Methode verwenden und Ergebnis auf der Standardausgabe ausgeben
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()); }}
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 -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.

DNS-Cache unter Windows und mit C# leeren 👍 👎

Um den DNS-Zwischenspeicher unter Windows zu leeren, gibt es den bekannten Befehl ipconfig /flushdns.

Prinzipiell wäre es zwar möglich, das Programm ipconfig mit dem Argument /flushdns über die Process-Klasse per C# aufzurufen, jedoch ist dies nicht ganz unproblematisch, da die Ausführung längere Zeit dauern kann.

Alternativ hilft uns hier – wieder einmal – die Windows-API in Kombination mit dem DllImport-Attribut:
(Hilfs-)Methode deklarieren und verwenden
01020304050607
public static class DnsUtility {    [DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]    public static extern void FlushCache();}
// DNS-Cache leerenDnsUtility.FlushCache();
Dies ist insbesondere in Verbindung mit der Dns-Klasse nützlich, um aktuelle Daten zu erhalten. Natürlich gibt es einen solchen Zwischenspeicher aber nicht grundlos, d. h. es ist nur äußerst selten ratsam, diesen immer wieder vor einer Namensauflösung zu leeren – Performanzdifferenzen im Rahmen einer Größenordnung sind hier üblich. Im Rahmen des Debuggings oder für besonders kritische Prozesse kann das Vorgehen jedoch durchaus sinnvoll sein.

GET- und POST-Anfragen mit C# 👍 👎

Für viele APIs – aber auch so manchen Test – ist es hilfreich, GET- und POST-Anfragen per HTTP senden zu können. Selbstverständlich ist das auch per C# sehr einfach möglich, wobei je nach gewünschtem Grad der Kontrollierbarkeit der Anfragen verschiedene Abstrahierungen zur Verfügung gestellt werden:

Die umfangreichsten Möglichkeiten (und geringen Abstraktiongrad) bietet HttpWebRequest:
GET per HttpWebRequest
01020304050607
HttpWebRequest webRequest = HttpWebRequest.CreateHttp("…/?key1=val1&key2=val2");
using(HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse()) { using(StreamReader streamReader = new StreamReader(webResponse.GetResponseStream())) { string response = streamReader.ReadToEnd(); }}
POST per HttpWebRequest
010203040506070809101112131415161718
HttpWebRequest webRequest = HttpWebRequest.CreateHttp("…");{    byte[] data = Encoding.UTF8.GetBytes("key1=val1&key2=val2");
webRequest.Method = "POST"; webRequest.ContentType = "application/x-www-form-urlencoded"; webRequest.ContentLength = data.Length;
using(Stream stream = webRequest.GetRequestStream()) { stream.Write(data, 0, data.Length); }}
using(HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse()) { using(StreamReader streamReader = new StreamReader(webResponse.GetResponseStream())) { string response = streamReader.ReadToEnd(); }}
Deutlich kompakter lassen sich entsprechende Anfragen per WebClient formulieren:
GET per WebClient
010203
using(WebClient webClient = new WebClient()) {    string response = webClient.DownloadString("…/?key1=val1&key2=val2");}
POST per WebClient
010203040506
using(WebClient webClient = new WebClient()) {    string response = Encoding.UTF8.GetString(webClient.UploadValues("…", new NameValueCollection() {        {"key1", "val1"},        {"key2", "val2"}    }));}
Die aktuellste Möglichkeit mit Fokus auf asynchrone Vorgänge stellt HttpClient dar:
GET per HttpClient
010203
using(HttpClient client = new HttpClient()) {    string response = await client.GetStringAsync("…/?key1=val1&key2=val2");}
POST per HttpClient
01020304050607080910
using(HttpClient httpClient = new HttpClient()) {    HttpResponseMessage responseMessage = await httpClient.PostAsync("…",        new FormUrlEncodedContent(new Dictionary<string, string>() {            {"key1", "val1"},            {"key2", "val2"}        })    );
string response = await responseMessage.Content.ReadAsStringAsync();}
Grundsätzlich bestünde auch noch die Möglichkeit, auf TCP-Ebene unter Anwendung des HTTP-Protokolls vorzugehen – aber das wird man kaum benötigen. Abschließend sei noch erwähnt, dass diese Möglichkeiten nicht für fragliche Aktionen genutzt werden sollte – dazu gehört im Übrigen auch schlicht unnötiger Traffic.

Papierkorb mit C# leeren 👍 👎

Passend zum letzten Beitrag (Datei mit C# in Papierkorb verschieben) soll es noch einmal um den Papierkorb gehen. Um den Papierkorb zu leeren gibt es leider ebenfalls keine in verwaltetem Code zur Verfügung stehende Funktionalität des .NET-Frameworks für C#. Es ist also – wieder einmal – an der Zeit für DllImport.

Wir werden dazu eine Klasse RecycleBin mit integrierten RecycleFlags erstellen:
RecycleBin.cs
010203040506070809101112131415161718
public static class RecycleBin {    public static void Clear(RecycleFlags recycleFlags = RecycleFlags.None) {        SHEmptyRecycleBin(IntPtr.Zero, null, recycleFlags);    }

[DllImport("shell32")] private static extern uint SHEmptyRecycleBin( IntPtr hwnd, string pszRootPath, RecycleFlags dwFlags );
public enum RecycleFlags : uint { None = 0x0, // Standardverhalten NoConfirmation = 0x1, // keine Rückfrage zur Bestätigung NoProgressUI = 0x2, // keine Fortschrittsanzeige NoSound = 0x4 // kein Abschlussgeräusch }}
Die Verwendung gestaltet sich nun wieder gewohnt einfach:
Papierkorb leeren
0102030405
  // Papierkorb leeren (Standard)RecycleBin.Clear();
// Papierkorb ohne Rückfrage leerenRecycleBin.Clear(RecycleBin.RecycleFlags.NoConfirmation);
Im MSDN gibt es wie üblich weitere Details zu SHEmptyRecycleBin.

12

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche