Rückgabe von iCal-Dateien mit ASP.Net Web API

Ihr kennt doch sicher alle die iCal-Dateien, oder? Bei einer Hotelbuchung oder der Buchung einer Zugfahrt, hat man oftmals die Option, sich den Zeitraum im Kalender speichern zu lassen. Das, was wir dann laden, ist unsere iCal-Datei die bei Ausführung einen Termin in unseren Kalender schreibt.

In diesem Beitrag möchte ich zeigen, wie man weitere Formate, in diesem Fall das iCal-Format, in ASP.Net Web API unterstützen kann.

Werfen wir doch zunächst einmal einen Blick auf den Aufbau einer iCal-Datei. Sie könnte zum Beispiel so aussehen:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:http://www.beispiel.com/kalender/
METHOD:PUBLISH
BEGIN:VEVENT
UID:224592315246@example.com
ORGANIZER;CN="Max Mustermann, Company Inc.":MAILTo:mustermann@company.com
LOCATION:Leipzig
SUMMARY:Eine Kurzinfo (Titel)
DESCRIPTION:Terminbeschreibung
CLASS:PUBLIC
DTSTART:20160518T070000Z
DTEND:20160520T223000Z
DTSTAMP:20160414T121500Z
END:VEVENT
END:VCALENDAR
 

Unsere Datei, die wir erstellen wollen, sollte im Optimalfall alle diese Einträge besitzen. Natürlich reichen auch nur die Grunddaten des Eintrages. In meinem Beispiel verwende ich ebenfalls nur die wichtigsten Eigenschaften.

Wir starten mit einem einfachem Model:

1 namespace BlogWebApi_iCal.Models 2 { 3 class CalModel 4 { 5 public int Id { get; set; } 6 7 public DateTime From { get; set; } 8 public DateTime Until { get; set; } 9 10 public string Title { get; set; } 11 12 public string Description { get; set; } 13 } 14 }

In diesem Beispiel arbeiten wir mit fünf Werten: Der ID, dem Startdatum, Enddatum, einem Titel und der Beschreibung.

Als nächstes benötigen wir eine neue Klasse. Diese Klasse wird von einem MediaFormatter erben. Genauer gesagt vom BufferedMediaTypeFormatter. Diese Klasse leitet sich vom MediaTypeFormatter ab, benutzt aber im Gegenteil dazu synchrone Lese- und Schreibemethoden.

Im Konstruktor der Klasse legen wir fest, welchen Datentyp wir unterstützen wollen. Haben wir das getan, sollte die Klasse so aussehen:

 

1 public class FormatterCalender : BufferedMediaTypeFormatter 2 { 3 public FormatterCalender() 4 { 5 SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/iCal")); 6 } 7 }

Im nächsten Schritt müssen einige Methoden überschrieben werden. VisualStudio sollte an dieser Stelle bereits was dazu anzeigen. Wir arbeiten zunächst an der CanWriteType-Methode, um zu erkennen, welchen Datentyp unserer Formatter serialisieren kann. Dann folgt die CanReadType-Methode. Da wir nicht derialisieren wollen, wird diese Methode einfach nur false zurückgeben. Fertig sieht das ganze so aus:

1 public class FormatterCalender : BufferedMediaTypeFormatter 2 { 3 public FormatterCalender() 4 { 5 SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/iCal")); 6 } 7 8 public override bool CanReadType(Type type) 9 { 10 return false; 11 } 12 13 public override bool CanWriteType(Type type) 14 { 15 if (type == typeof(CalModel)) 16 { 17 return true; 18 } 19 else 20 { 21 Type enumerableType = typeof(IEnumerable<CalModel>); 22 return enumerableType.IsAssignableFrom(type); 23 } 24 } 25 }

Sowohl einzelne Kalender-Einträge als auch Listen können verarbeitet werden.

Was wir jetzt noch brauchen, sind zwei weitere Methoden. Zunächst überschreiben wir die WriteToStream-Methode. Diese Methode schreibt synchron in den gepufferten Datenstrom. Hier findet nun die Serialisierung statt. Die Eintrage der iCal-Datei werden in den Stream geschrieben.

1 public override void WriteToStream(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content) 2 { 3 using (var writer = new StreamWriter(writeStream)) 4 { 5 writer.WriteLine("BEGIN:VCALENDAR"); 6 writer.WriteLine("VERSION:2.0"); 7 var events = value as IEnumerable<CalModel>; 8 if (events != null) 9 { 10 foreach (var e in events) 11 { 12 WriteCal(e, writer); 13 } 14 } 15 else 16 { 17 var singleEvent = value as CalModel; 18 if (singleEvent == null) 19 { 20 throw new InvalidOperationException("Cannot serialize"); 21 } 22 WriteCal(singleEvent, writer); 23 } 24 writer.WriteLine("END:VCALENDAR"); 25 } 26 writeStream.Close(); 27 }

 

Innerhalb dieser Methode wird eine weitere aufgerufen. Was hier geschrieben wird, ist nur der Grundriss der iCal-Datei. ´Solange ein Eintrag vorhanden ist und somit kein NULL bei rauskommt, wird die Methode WriteCal aufgerufen. Dies geschieht in der foreach-Schleife als auch im else-Zweig, wo einzelne Einträge bearbeitet werden. Um die restlichen Werte einzutragen, schreiben wir einfachheitshalber eine zusätzliche, kleine Methode.

1 //Helfer Methode zum Serialisieren 2 private void WriteCal(CalModel singleEvent, StreamWriter writer) 3 { 4 writer.WriteLine("BEGIN:VEVENT"); 5 writer.WriteLine("UID:" + singleEvent.Id); 6 writer.WriteLine("DTSTART:" + string.Format("{0:yyyyMMddTHHmmssZ}", singleEvent.From)); 7 writer.WriteLine("DTEND:" + string.Format("{0:yyyyMMddTHHmmssZ}", singleEvent.Until)); 8 writer.WriteLine("SUMMARY:" + singleEvent.Title); 9 writer.WriteLine("END:VEVENT"); 10 }

 

Damit wird die iCal-Datei erstellt. Die Werte, welche im Event eingetragen sind, werden vollständig in der WriteCal-Methode in den Stream geschrieben.

Ein letzter, ganz kleiner Schritt fehlt noch. Damit alles ohne weiteres funktioniert, fügen wir den Formatter dem configuration object hinzu. Dazu wechseln wir in die WebApiConfig.cs und ergänzen dort eine Zeile:

1 public static class WebApiConfig 2 { 3 public static void Register(HttpConfiguration config) 4 { 5 config.Routes.MapHttpRoute( 6 name: "DefaultApi", 7 routeTemplate: "api/{controller}/{id}", 8 defaults: new { id = RouteParameter.Optional } 9 ); 10 config.Formatters.Add(new FormatterCalender()); 11 } 12 }

 

Durch diese eine Zeile, wird es immer automatisch aufgerufen, wenn ein Client das iCal-Format akzeptiert. Jetzt können wir es zum Beispiel mit dem PostMan testen und bekommen eine iCal-Datei zurück.

Ich hoffe, es war hilfreich für euch und wir lesen uns im nächsten Eintrag.

Kommentare sind geschlossen