Ein Tech-Stack für Microservices ist genug

Über Microservices gibt es zahlreiche Bücher und noch viel mehr Artikel. Darin finden sich darin auch viele Erfahrungen, Tipps, Meinungen und jede Menge Vorteile. Als einer dieser Vorteile taucht gerne die Wahlfreiheit der verwendeten Sprache/Plattform auf. Darauf möchte ich in diesem Beitrag näher eingehen.

Grundaussage

Ein Microservice ist eigenständig. Es läuft in einer passenden Umgebung. Alleine, oder zusammen mit anderen Microservices. Jedes Microservice kann für eine andere Plattform/Programmiersprache (C++, Java, .NET, Haskell, Rust, etc.) entwickelt werden.

Die Vorteile:

  • Die Umsetzung kann mit der Plattform/Programmiersprache umgesetzt werden, mit der sich die Anforderungen am besten abbilden lassen.

  • Softwareentwickler müssen nicht mehr dieselbe Plattform/Programmiersprache lernen, sondern können ein Microservice mit bereits vorhandenen Kenntnissen umsetzen. Das spart Zeit und bietet Vorteile bei der Suche neuer EntwicklerInnen.

Was bedeuten diese Vorteile in der Realität?

Machbarkeit/Realitätsnähe

Bei der Mitarbeitersuche nicht mehr auf eine Plattform Rücksicht nehmen zu müssen, klingt gut. Gerade in Zeiten, in denen jeder Softwareentwickler umkämpft, wie nie zuvor, ist.

Für das Unternehmen (und in weiterer Folge auch den Mitarbeitern) ergibt sich jedoch ein anderes Bild:

  • Neue Plattformen benötigen zusätzliches Know-how. Eine Entwicklungsmaschine ist schnell eingerichtet, für den Produktivbetrieb gelten andere Anforderungen hinsichtlich Administration, Konfiguration, Sicherheit usw. Es entstehen zusätzliche Kosten und Risiken.

  • Wer pflegt und führt die Weiterentwicklung fort, wenn der ursprüngliche Entwickler nicht (mehr) verfügbar ist? Durch Krankheit, Unfall oder Kündigung kann ein erheblicher Aufwand durch Know-how-Verlust auf das Unternehmen zukommen. Nicht selten wird Software neu entwickelt, weil dies insgesamt günstiger kommt.

  • Um Themen wie Performance-Optimierungen etc. bewältigen zu können, bedarf es tiefgreifenden Wissens über den Technologiestack. Denselben Grad an Tiefe über mehrere Plattformen hinweg aufzubauen und dabei auch am aktuellen Stand zu bleiben, ist eine aufwändige und damit kostenintensive Angelegenheit – und auf lange Frist in der Regeln nicht machbar.

Bei einem Blick auf die Unternehmensverteilung in Österreich, sind 99,8% der Unternehmen laut WKO im Jahr 2017 99,8% der Unternehmen den KMU zuzuordnen (0-249 Mitarbeiter). In Deutschland sieht dieses Bild ähnlich aus. Hier sind es laut Statista 99,6%.

Internationale Großkonzerne sind aufgrund der vorhandenen Mittel im Vorteil. Einschlägige Softwareunternehmen an der oberen Grenze, die zudem mit einem einzigen Produkt am Markt sind, könnten dies ebenfalls bewältigen. Der Rest sollte tunlichst die Finger davon lassen.

Fazit

Nicht jeder genannte Vorteil, entpuppt sich als solcher. Microservices haben ihre Daseinsberechtigung. Einzelne Services auf unterschiedlichen Plattformen aufsetzen zu können, kann in manchen Fällen tatsächlich sinnvoll und hilfreich sein. Tatsächlich muss dies im Sinne der Wirtschaftlichkeit hinterfragt werden und keinesfalls frei von jedem Entwickler im Unternehmen entschieden werden können.

The importance of automated software testing

Much has already been written about automated software testing – by me as well. Still there are so many developers out there, who say automated tests only costs money and time and so they rather put their time into building more functionality. Because this is, what the customer needs – they think. Some developers even think they are godlike, their code can’t have any defects, so there is no need for automated software testing. My experience tells another story though …

Automated testing is so expensive

Many years ago, I was developing a SaaS solution with a team of developers. We created a monolithic service having many features, but we wrote no automated tests. Every three or four months there was a “testing phase”. Everyone in the company (~ ten people) had to test the software the whole daylong. This lasted for about two weeks and resulted in lists of hundreds of bugs. The following few weeks were used to fix all those bugs, no new features were implemented.

Some years later, we decided to move to a new technology for our user interface (ASP.NET web forms to AngularJS). As part of this change my team got the chance to transform the monolithic application into several microservices. As the lead of this team, I promoted the implementation of automated tests. We wrote unit tests as well as integration tests for our web API endpoints. Even our Angular front-end code had been tested. Each team member had to run all tests before committing changes to our source control.

What do you think was the result of introducing automated tests?

We only had three to four bugs a month reported by colleagues or customers and could focus on building new features and a better user experience, improve performance and do research. No more testing and bug fixing weeks. That saved us so much money! Unbelievable. And we really loved it.

But writing tests costs time/money

This is the number one argument. Of course, it does! But: The more tests you write and the better you get at it, the less time you need. You will not only learn how to write tests very fast, you’ll also gain a sense for what is important to test.

So, writing unit and/or integration tests costs money. Right now. However, it saves money in the future. Neglecting to write tests saves money now, but costs dearly in the future. Let us have a look into the details.

Costs for writing tests

Again, writing tests needs time. However, every test helps to avoid failures in future. Of course, bugs can arise and you have to fix them, but you can do that in a test-driven manner. Write a test which assumes the correct behavior, then fix the bug until everything is fine and the test completes successfully. This failure will not bother you anymore in future.

Depending on your software, your test coverage and the used types of automated tests, you still may need to perform manual tests as well though.

Costs for not writing tests

In case you don’t want to deliver untested software, you have to do manual testing to find bugs. For these tests, detailed test cases are necessary. In reality, they too hardly exist if there was a decision against automated testing – costs are the basis of such decisions.

Doing manual tests is very time-consuming. It is reasonable to test manually even if you have automated tests, but the expenditure of time will be much higher if there are no automated tests.

Only relying on manual tests cause some negative effects (and they are cost-effective):

  • The software developer has to try to understand the code again when bugs are found weeks or months after the implementation. The sooner the bug was found, the better.
  • Generally, more bugs will be reported by customers if there is no automated testing. This ties up a lot of resources on the customers side and of course also at your own company (someone has to file the ticket, try to reproduce the bug, manage development planning, another testing phase has to be planned, a new release has to be scheduled and delivered, release notes have to be communicated and so forth).
  • In case of unit tests, the software design will be clearer and coupling will be lower. If you work actively with unit tests you tend to be in intensive care of the software design because you think about usage of your classes and methods. This leads to less complexity and higher maintainability.

Deployment could be relatively easy in case of a SaaS solution but that would become much more complex (and expensive) if you have a lot of on premises installations, especially in complex industrial infrastructures.

Positive side effects

I run all tests before committing my changes to the source control. This provides instant feedback and things can get fixed immediately.

Automated tests are an investment in the future of your software as well as your company. Someday a refactoring will be necessary. Automated tests greatly minimize the risk that accidental behavior changes occur, even when the software design or the implementation changes.

There is another cool thing about automated tests: Run them with a nightly build and log the execution times into a database. This makes it possible to trace performance of your software. Doing this daily provides a great feedback about the impact of the implementations made.

Conclusion

In reality, it is much more expensive to neglect to write tests. Some ignore the fact that the costs will incur much later. They just see less cost in development, but this is – in my opinion – a big mistake. My advice is to write both unit tests whenever reasonable, as well as integration tests for your entire API.

Vorgestellt: Synology Note Station

Während die einen alle ihre Daten in die Cloud packen, gehen andere bewusster mit ihren Daten um. Sie suchen sich Lösungen, die sie nicht zur Abgabe der Kontrolle über ihre Daten zwingen. Wer ein System von Synology besitzt, bekommt eine solche Lösung frei Haus: Note Station.

Note Station wird auf dem eigenen Synology-Gerät installiert und stellt dann ein Service für die Verwaltung von benutzerbezogenen Notizen und Aufgaben zur Verfügung.

Funktionsumgang

Mittlerweile ist die Note Station nicht mehr ganz so jung und der Fuktionsumfang kann sich richtig sehen lassen:

Notizen

  • Notizbücher
  • Suchfunktion
  • Tagging
  • Attachments
  • Inline-Bilder
  • Audio-Aufnahmen
  • Diagramme
  • Verschlüsseln von Notizen
  • Anzeige im Präsentationsmodus

Interessant sind auch die Smart-Notizbücher. Diese können mit Schlagwörtern versehen werden und suchen alle passenden Notizen aus den vorhandenen Notizbüchern.

Aufgaben

  • Erinnerungen
  • Favoriten-Markierung
  • Unteraufgaben
  • Prioritäten
  • Integration zu Notizen

Neben einer Desktop-Version steht auch eine App für mobile Endgeräte (Android und iOS) zur Verfügung. Darüber steht ein Offlinemodus zur Verfügung.

Synology Note Station | Norbert Eder

Teilen

Notizen können natürlich auch mit anderen Personen geteilt werden. Dies ist sowohl für am System vorhandenen Benutzer bzw. Gruppen möglich, als auch extern. Das externe Teilen funktioniert über einen Teilen-Link. Ein externer Zugriff auf das Synology-Gerät musste zuvor über die System-Einstellungen eingerichtet werden.

Apps

Neben Apps für den Desktop (Windows und Mac), stehen auch entsprechende Apps für Android und iOS zur Verfügung. Die Desktop-App ist über das Download-Center verfügbar, die mobilen Apps über die jeweiligen App-Stores.

Synchronisation

Die Synchronisations-Möglichkeiten sind abhängig vom eingerichteten System. Ist die Synology-Station nur im eigenn Netzwerk verfügbar, kann auch nur darin synchronisiert werden. Ist eine externe Nutzung (z.B. via QuickConnect) eingerichtet, erfolgt eine Synchronisation überall. Die Synchronisation erfolgt problemlos und zuverlässig.

Einschränkungen

Ein Feature fehlt mir wirklich sehr: die Archivierung einzelner Notizen. Es können ganze Notizbücher archiviert werden, einzelne Notizen hingegen nicht. Dies bedeutet mit der Zeit entweder eine lange Liste von Notizen im Notizbuch oder man verschiebt diese in ein eigenes Notizbuch. Alternativ werden alte/aufgearbeteite Notizen gelöscht, was nicht immer wünschenswert ist.

Zudem würde ich mir eine Markdown-Unterstützung wünschen.

Fazit

Wer keine klassischen Cloud-Lösung für die Verwaltung seiner Notizen, Audioaufnahmen, Aufgaben etc. verwenden möchte und zudem ein Synology-System zur Verfügung hat, der findet in der Note Station eine zuverlässige Lösung. In der täglichen Verwendung gibt es keine Einschränkungen was den Zugriff, die Synchronisation und die Zusammenarbeit betrifft.

Rezension: The Four – Die geheime DNA von Amazon, Apple, Facebook und Google

The Four - Die geheime DNA von Amazon, Apple, Facebook und Google Was macht Amazon, Google, Apple und Facebook aus? Warum haben sie diese derartige Größe und marktbeherrschende Situation erlangt? Nach welchen Maßstäben werden Produkte so derartig beliebt? Welche Strategien wenden diese Unternehmen an und was ist eigentlich ihr Kerngeschäft?

Das Buch The Four – Die geheime DNA von Amazon, Apple, Facebook und Google versucht diese und viele weitere Fragen zu beantworten. Ein Blick hinter die Kulissen zeigt, was diese Unternehmen im Hintergrund tun und was davon tatsächlich vermarktet wird bzw. wie Informationen dargestellt werden. Interessant ist zudem der Blick auf die angewandten Strategien der vier Unternehmen. Auch die engste Konkurrenz wird beleuchtet und deren Chancen bewertet – allerdings bezieht sich dies nur auf US-amerikanische Unternehmen (abgesehen von Alibaba).

Dieses Buch bietet viele interessante Aspekte und einen – wenngleich manchmal auch reißerischen Blick hinter die Fassade. Getrübt wird das Leseerlebnis durch häufiges Wiederholen derselben Aussagen. Um ein Drittel könnte das Buch so wohl gekürzt werden. Insgesamt liest es sich flüssig und bietet gute Informationen und vor allem sehr viele weiterführende Quellen, welche die Ausführungen untermauern bzw. für Überprüfungen herhalten können. Der Schreibstil ist etwas ungewöhnlich, aber man kommt relativ schnell hinein.

Grundsätzlich ist es kein Fehler, dieses Buch zu lesen. Es gibt allerdings einige Punkte, die man deutlich verbessern hätte können: Das Thema sind große, globale Unternehmen. Die Sichtweise ist jedoch hauptsächlich auf den US-amerikanischen Markt beschränkt. Das ist zwar grundsätzlich verständlich, passt aber eben nicht zum globalen Aspekt des Gegenstandes. Die bereits erwähnten Wiederholungen könnten mit noch detaillierteren Informationen ersetzt werden. Zudem sind einige Gedanken unsortiert und wirken dadurch etwas unausgegoren oder einfach nur fehl am Platz. Die Karrieretipps im letzten Kapitel kann man – aus meiner Sicht – getrost weglassen. Der Großteil steht bereits in den vorderen Kapiteln und der Rest funktioniert für den Großteil der Arbeitnehmer am europäischen Markt ohnehin nicht (bzw. sind die Tipps nicht zulässig).

Alles in allem bietet das Buch interessante Aspekte, Hintergrundinformationen und Analysen. Wer sich tiefgreifend mit den Unternehmen beschäftigt hat, wird das meiste wohl kennen. Wer Interesse an den Unternehmen hat, sich aber noch nicht die Zeit für Recherchen nahm, der findet hier sicherlich ein gutes Buch. Zwingend muss man es nicht lesen.

Rezension: Real Leaders Don’t Follow

Real Leaders Don't Follow Was macht einen guten Leader aus? Was tut er, was soll er tun, was sollte er können? Ich hatte so mein Bild eines Leaders und dachte, dass mir, Real Leaders Don’t Follow wird mir weiterhelfen, dieses Bild zu schärfen.

Aus meiner Sicht, geht es hauptsächlich darum, anderen ein Vorbild zu sein und ihnen einen klaren Weg vorzugeben. Einen Weg, der sinnvoll erscheint und allen Beteiligten Mehrwert verspricht. Der Kunde möchte seine Prozesse optimieren, sein Potential ausschöpfen, neue Möglichkeiten erhalten. Die eigenen Kollegen möchten etwas Sinnvolles erschaffen, etwas, dauch gebraucht wird (sich verkaufen lässt) und das in einer technisch sauberen Art.

After all, the goal of the book is to get you to formulate your own views and make smart decisions based on real-world experience. Implicit in that concept is that you follow no one’s doctrines—including mine.

Wozu dann aber über 300 Seiten durchkaufen? Das Buch ist gespickt mit Erfahrungen, unterschiedlichen Sichtweisen und Beispielen. Sie helfen, das eigene Gefühl zu schärfen.

If there’s one lesson I learned the hard way, it’s that you never get ahead by following anyone down a gilded path.

Ratschläge sind von diesem Buch nicht zu erwarten, wie sollten diese auch für die eigene Situation passend sein? Vielmehr vermittelt es eine überaus anregende Perspektive auf das gesamte Thema.

Inspiration doesn’t come from outside you. It comes from inside. It comes from your emotions, your passion. That’s what drives you. That’s what inspires you.

Selten habe ich ein Businessbuch gelesen, das eine Empfehlung so verdient hat. Deshalb möchte ich auch gar nicht zu viel verraten. Lies es selbst, es macht sich bezahlt.

Finde weitere hilfreiche Rezensionen oder folge mir auf goodreads.

How to test your web API with .NET Core

It’s so important to test your software automatically. Unit Tests are really great for testing small, atomic parts. But you have to ensure that your complete systems works as expected. This is where web API tests comes into play.

First, some words about the motivation behind this article: A lot of people don’t want to write test code and one of the reasons most state first is that they have too little time for it. In reality, tests reduce maintenance costs enormously – not only for the development team , but also for support or DevOps team. The most important thing to do is, to integrate your tests into your CI tool to execute them automatically and periodically.

There are several starting points into this topic. Let’s have a look.

Available API specification

Some of you might specify your web API using API Blueprint or Swagger. Both provides a high-level API description language. Those enable you to generate your web API as well as your tests from the API specification.

There are – of course – reasons to create code, but it’s not advisable to generate critical parts of a web API automatically. To create the tests automatically can be a good thing though – certtain limitations apply, which will be discussed further on.

Using tools to document API

Very often you have to work with an existing web API, but got no documentation about it. To get “help” some install tools like Swagger to generate documentation at runtime. This is pretty cool to get an overview of all existing endpoints.

For Swagger you can use Swashbuckle.AspNetCore. To install the package run

dotnet add package Swashbuckle.AspNetCore

To generate tests another packages needs to be added by running:

dotnet add package Swashbuckle.AspNetCore.Examples

This gives you the possibility to add examples for each endpoint, which is necessary to generate the tests. To achieve this, you have to implement several interfaces have to be implemented, and that . Ayields s a result you have a lot of source code to be able to generate tests from your API documentation.

So there are two possible solutions:

  • Take the effort and create a specification based on your existing API. As a benefit, your will be able to automatically generate tests from that specification.
  • Implement your tests manually.

Existing API without any documentation

Not everyone knows about API specifications, Open API and so on. A lot of developers just write their APIs. You can do that, but you won’t have any specifications, which also makes it harder for third parties (like buisness partners etc.) to your your API.

Of course, you can create a specification later, but doing this upfront saves a lot of effort. By using tools like Swagger, you are able to create at least a helpful documentation of your web API. To generate your tests automatically from this documentation needs a lot of additional source code. That’s not to be preferred.

You have two possibilities:

  • Create your own coded testing project
  • Script something using tools like HTTPie and bash etc.

Creating a testing infrastructure by code

There are important reasons to also implement testing code manually:

  • It’s not enough to just test your API against its specification, you also need to know if your implementation does really work
  • Testing APIs also requires to test (business) workflows. This can’t be done by tests that were created automatically for single endpoints.

For my tests I use XUnit. This is – originally – a unit testing framework, but it also provides interfaces for ordering your tests. Ordered tests are necessary to test workflows.

First, you need to implement an attribute to be able to order your tests:

public class TestOrderAttribute : Attribute
{
  public int Priority { get; set; }

  public TestOrderAttribute(int priority)
  {
    Priority = priority;
  }
}

Then you need a mechanism to order your tests within a single test case:

public class TestPriorityOrderer : ITestCaseOrderer
{
  public IEnumerable<ttestcase> OrderTestCases<ttestcase>(IEnumerable<ttestcase> testCases) where TTestCase : ITestCase
  {
    SortedList<int ttestcase="ttestcase"> sortedTestCases = new SortedList<int ttestcase="ttestcase">();
    foreach (var testCase in testCases)
    {
      var methodInfo = testCase.TestMethod.Method;
      var attribute = methodInfo.GetCustomAttributes((typeof(TestOrderAttribute).AssemblyQualifiedName)).FirstOrDefault();
      var priority = attribute.GetNamedArgument<int>("Priority");
      sortedTestCases.Add(priority, testCase);
    }
   return sortedTestCases.Values.ToList();
  }
}

Last but not least, you need to order all existing test cases:

public class TestCollectionOrderer : ITestCollectionOrderer
{
  public IEnumerable<itestcollection> OrderTestCollections(IEnumerable<itestcollection> testCollections)
  {
    return testCollections.OrderBy(GetOrder);
  }

  private static int GetOrder(ITestCollection testCollection)
  {
    var i = testCollection.DisplayName.LastIndexOf(' ');
    if (i <= -1)
    return 0;

    var className = testCollection.DisplayName.Substring(i + 1);
    var type = Type.GetType(className);
    if (type == null)
      return 0;

    var attr = type.GetCustomAttribute<testorderattribute>();
    return attr?.Priority ?? 0;
  }
}

At a single point within your testing assembly set necessary configurations:

[assembly: collectionbehavior(disabletestparallelization = true)][assembly: testcollectionorderer("integrationtests.infrastructure.testcollectionorderer", "integrationtests")]
[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: TestCollectionOrderer("TestCollectionOrderer", "IntegrationTests")]

To configure your test class, set the test case orderer as well as the test order:

[TestCaseOrderer("TestPriorityOrderer", "IntegrationTests")]
[TestOrder(10)]
public class AuthenticationTest { ... }

Your test methods (facts) have to be configured as well:

[Fact, TestOrder(20)]
public async void LoginWithCorrectCredentials_ShouldBeOk()

Alright, this is all you need to run your web API tests.

You will gain the bests results when you combine the requirement that all tests need to run successfully BEFORE new code is commited into your source repository, with also setting up automatic test runs within your CI infrastructure.

Happy coding!

Web API und Datenbankzugriff mit Go

Go aka golang wurde hauptsächlich entwickelt, um Concurrency optimal zu unterstüzten. Daher bietet sich an, eine Web API mit Go zu entwickeln. Dieser Beitrag zeigt, was dafür notwendig ist und dass dafür nicht sonderlich viel Aufwand eingesetzt werden muss.

Folgendes möchte der Beitrag zeigen:

  1. Web API mit versionierter API-Entstelle, JSON wird als Datenformat verwendet; Die Web API kann eine Liste von Kontakten zurückgeben, sowie neue Kontakte erstellen
  2. Interaktion mit einer MySQL-Datenbank

Notwendiges Vorwissen

  • Grundlagen Go / Umgebung aufsetzen und ausführen können, Verwendung von Paketen und Modulen
  • Grundlagen Web APIs
  • JSON
  • MySQL Schema und Tabelle anlegen können

Tabelle erzeugen

Für dieses Beispiel wird eine Tabelle benötigt, dies ist mit folgendem Script anzulegen:

CREATE TABLE `contact` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `firstName` varchar(50) DEFAULT NULL,
  `lastName` varchar(50) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Im Beispiel wird von einem existierenden Schema contacts ausgegangen.

Verwendete Packages

Mit dem Paket net/http, welches mit Go ausgeliefert wird, können Routen grundsätzlich implementiert werden. Wer es etwas komfortabler haben möchte, verwendet github.com/gorilla/mux. Dadurch kann beispielsweise einfacher mit HTTP-Verbs gearbeitet werden.

Für den Zugriff auf MySQL wird github.com/go-sql-driver/mysqleingesetzt. Dies setzt auf das bereits enthaltene Paket database/sql auf.

Zugriff auf MySQL

Mittels db.go wird die Initialisierung der Datenbank zur Verfügung gestellt:

package models

import (
    "database/sql"
    "log"
    _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func InitializeDatabase(connectionString string) {
    log.Println("Initializing database ...")
    var err error
    db, err = sql.Open("mysql", connectionString)
    if err != nil {
        log.Panic(err)
    }

    if err = db.Ping(); err != nil {
        log.Panic(err)
    }
}

In contacts.go wird sowohl der Typ Contact zur Verfügung gestellt, als auch die Funktionen für den Umgang mit der Datenbank:

package models

import "log"

type Contact struct {
    Id int `json:"id"`
    FirstName string `json:"firstName"`
    LastName string `json:"lastName"`
    Email string `json:"email"`
}

func AllContacts() ([]*Contact, error) {
    rows, err := db.Query("SELECT id, firstName, lastName, email FROM contact")
    if err != nil {
        return nil, err
    }

    log.Println("Successfully loaded contacts from database")
    contacts := make([]*Contact, 0)
    for rows.Next() {
        contact := new(Contact)
        err := rows.Scan(&contact.Id, &contact.FirstName, &contact.LastName, &contact.Email)
        if err != nil {
            return nil, err
        }
        contacts = append(contacts, contact)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }

    defer rows.Close()

    return contacts, nil
}

func InsertContact(contact *Contact) (error){
    result, err := db.Exec("INSERT INTO contact (firstName, lastName, email) VALUES (?,?,?)", contact.FirstName, contact.LastName, contact.Email)
    if err != nil {
        return err
    }

    id, _ := result.LastInsertId()

    contact.Id = int(id)

    return nil
}

Verarbeitung der API-Calls

Da nun der Datenbank-Zugriff geregelt ist, wenden wir uns der Kommunikation über HTTP zu. Es muss entsprechende Funktionen für die Rückgabe der bereits vorhandenen Kontakte, sowie zur Anlage geben.

package controller

import (
    "net/http"
    "sampleWebApi/models"
    "encoding/json"
    "log"
)

func AllContacts(w http.ResponseWriter, r *http.Request) {
    log.Println("Endpoint AllContacts")
    contacts, err := models.AllContacts()
    if err != nil {
        log.Fatal(err)
        http.Error(w, http.StatusText(500), 500)
        return
    }
    json.NewEncoder(w).Encode(contacts)
    log.Println("Sent all contacts")
}

func InsertContact(w http.ResponseWriter, r *http.Request) {
    log.Println("Endpoint InsertContact")
    var c models.Contact
    if r.Body == nil {
        http.Error(w, "Please send any body", http.StatusBadRequest)
        return
    }
    err := json.NewDecoder(r.Body).Decode(&c)
    if err != nil {
        log.Fatal(err)
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    err = models.InsertContact(&c)
    if err != nil {
        log.Fatal(err)
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
    w.WriteHeader(http.StatusOK)
    log.Println("Inserted new contact")
}

Routing und Verbinden der Teile

In der main.go werden nun die Einzelteile miteinander verbunden. So erfolgt im ersten Schritt die Initialisierung der Datenbank, danach wird das Routing (und somit die möglichen API-Endpunkte) konfiguriert:

package main

import (
    "net/http"
    "log"
    "github.com/gorilla/mux" // specify used verbs
    "sampleWebApi/models"
    "sampleWebApi/controller"
)

func defaultPage(w http.ResponseWriter, r *http.Request) {
    log.Println(w, "Endpoint hit")
}

func handleRequests() {
    log.Println("Handling requests ...")
    router := mux.NewRouter().StrictSlash(true)

    router.HandleFunc("/", defaultPage)
    router.HandleFunc("/api/1/contacts", controller.AllContacts).Methods("GET")
    router.HandleFunc("/api/1/contacts", controller.InsertContact).Methods("POST")
    log.Fatal(http.ListenAndServe(":8081", router))
}

func main() {
    models.InitializeDatabase("user:pass@tcp(127.0.0.1:3306)/contacts")
    handleRequests()
}

Download

Dieses Beispiel steht auf GitHub zur Verfügung und kann frei verwendet werden. Entwickelt und getestet wurde es mit Go 1.10.1. Docker-Support inklusive.

Happy coding!

ASP.NET Core und Custom Formatter

Der Datenaustausch kann in ASP.NET Core MVC per JSON, XML bzw. reinem Text geschehen. Dies ist allerdings nicht für alle Fälle ausreichend. Daher kann man selbst eingreifen und für zusätzliche Unterstützung sorgen. Dieser Artikel zeigt, wie man das iCalendar-Format unterstützt.

Grundlagen

Formatter werden verwendet, um Inhaltsanfragen (siehe Accept-Header – Stichwort Content Negotiation) zu verarbeiten. Das Standard-Format in ASP.NET Core MVC ist JSON (application/json). Sendet der Client einen Accept-Header mit einem unbekannten Inhaltstyp, erfolgt eine Umleitung auf den Standard-Typ. Das ist (sofern keine Änderung erfolgte), JSON. Die Verwendung von XML muss explizit konfiguriert werden (siehe Startup.cs):

services.AddMvc()
    .AddXmlSerializerFormatters();

Formatter können für beide Richtungen, also Input und Output, implementiert werden. Ebenfalls gibt es die Möglichkeit per Text (siehe TextInputFormatter und TextOutputFormatter), sowie per Streams (siehe StreamInputFormatter und StreamOutputFormatter) zu arbeiten.

Konkrete Implementierung

Im Beispiel soll es folgende Möglichkeiten geben:

  1. Rückgabe eines Termins als JSON
  2. Rückgabe eines Termins im iCalendar-Format
  3. Direktaufruf Link ohne eigenen Accept-Header für Download

Da nur Daten zurückgegeben, aber nicht angenommen werden, ist nur ein Output-Formatter zu implementieren. Der nachfolgende Code zeigt die Details. Im Konstruktor werden die unterstützten MediaTypes angegeben.

public class IcsOutputFormatter : TextOutputFormatter
{
    public IcsOutputFormatter()
    {
        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.Unicode);

        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/x-ical"));
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/ical"));
    }

    protected override bool CanWriteType(Type type)
    {
        // this should only be available for appointments
        if (typeof(Appointment).IsAssignableFrom(type) 
            || typeof(IEnumerable<Appointment>).IsAssignableFrom(type))
        {
            return base.CanWriteType(type);
        }

        return false;
    }

    public override Task WriteResponseBodyAsync(    
        OutputFormatterWriteContext context, 
        Encoding selectedEncoding
    )
    {
        IServiceProvider serviceProvider = context.HttpContext.RequestServices;
        var logger = serviceProvider.GetService(typeof(ILogger<IcsOutputFormatter>)) as ILogger;

        var response = context.HttpContext.Response;

        var buffer = new StringBuilder();
        if (context.Object is IEnumerable<Appointment>)
        {
            foreach (Appointment appointment in context.Object as IEnumerable<Appointment>)
            {
                FormatIcs(buffer, appointment, logger);
            }
        }
        else
        {
            var appointment = context.Object as Appointment;
            FormatIcs(buffer, appointment, logger);
        }
        return response.WriteAsync(buffer.ToString());
    }

    // more code
}

In der Methode CanWriteType wird auf einen konkreten Typ eingeschränkt. Zugelassen wird werden alle Objekte, die ein Appointment oder eine Liste davon darstellen.

Sollen auch Daten im iCalendar-Format empfangen und verarbeitet werden können, müsste ein Input-Formatter implementiert werden. Dazu wäre eine Ableitung von TextInputFormatter vorzunehmen.

Konfiguration

In der Methode ConfigureServices wird der neue IcsOutputFormatter gesetzt. Ebenfalls wird ein MediaType-Mapping von ics auf application/x-ical gesetzt. Darüber wird die Extension ics in der URL ermöglicht. Wird diese gesetzt, erfolgt eine Behandlung, für den dazu gemappten MediaType, also unserem Formatter.

services.AddMvc(options =>
{
    options.OutputFormatters.Insert(0, new IcsOutputFormatter());
    // for accessing via URL
    options.FormatterMappings.SetMediaTypeMappingForFormat("ics", "application/x-ical");
});

Damit das MediaType-Mapping funktioniert, müssen die dafür in Frage kommenden Methoden mit zusätzlichen Attributen versehen werden:

[Route("api/1/appointment"), EnableCors("AllowAllOrigins")]
public class AppointmentController : Controller
{
    [FormatFilter]
    [HttpGet("{id}"), HttpGet("{id}.{format}")]
    public Appointment Get(int id)
    {
        var organizer = new User()
        {
            FirstName = "Norbert",
            LastName = "Eder",
            Organization = "NE",
            Email = "thisismyemail@herewego"
        };
        return new Appointment()
        {
            IsPublic = true,
            Organizer = organizer,
            Description = "Wall of text",
            Title = "So important",
            Location = "Vienna",
            Start = DateTime.Now,
            End = DateTime.Now.AddHours(2)
        };
    }
}

Das FormatFilterAttribute definiert, dass ein format-Wert der Route verwendet wird. So wird im Beispiel die Route mit{id}.{format} definiert. d.h. mit /api/1/appointment/1.ics werden die Daten im iCalendar-Format zurückgeliefert.

Das FormatFilterAttribute erzwingt die format-Angabe. Damit dies auch ohne möglich wird, kann die Route zweimal konfiguriert werden:

[HttpGet("{id}"), HttpGet("{id}.{format}")]

Nun sind beide Varianten möglich. Ohne Format wird die Default-Verarbeitung angeworfen. In der Regel wird also JSON zurückgegeben.

Demo-Anwendung

Das gezeigte Beispiel steht via GitHub zur Verfügung und zeigt den gezeigten IcsOutputFormatter in Aktion.

Viel Spaß beim Entwickeln!

Rezension: Wie sich Menschen organisieren, wenn ihnen keiner sagt, was sie tun sollen

Wie sich Menschen organisieren, wenn ihnen keiner sagt, was sie tun sollen - Lars Vollmer Dieses (kurze) Buch mit dem langen Titel Wie sich Menschen organisieren, wenn ihnen keiner sagt, was sie tun sollen von Lars Vollmer vermittelt interessante Informationen und Gedanken zu sich selbst organisierenden Organisationen. Was treibt Menschen an, Initiative zu übernehmen, ja, überhaupt etwas zu tun? Woher wissen sie, was gerade am Wichtigsten ist? Wann scheitert Planung, wann ist sie sinnvoll? Und was ist das Futter einer derartigen Organisation?

Das Buch vermittelt keine allumfassende Lösung, sondern gute und umsetzbare Denkanstöße. Es ist kein Plädoyer für den grundsätzlichen “Einsatz” von sich selbst organisierenden Organisationen. Vielmehr vermittelt es ein gut ausbalanciertes Gefühl für dieses Thema. Ein flüssiger Schreibstil lässt zudem die Seiten nur so dahinfliegen – und macht Lust auf mehr.

Rezension: Komm zum Punkt!

Komm zum Punkt! - Thilo Baum Wer glaubt, er drücke sich klar aus, muss dieses Buch lesen. Komm zum Punk!: So drücken Sie sich klar aus von Thilo Baum zeigt auf, wie sorglos wir mit unserer Sprache umgehen. Marketing-Floskeln prasseln nicht nur auf uns ein, sondern wir setzen sie auch im Umgang mit anderen immer stärker ein. Wir kommen nicht mehr auf den Punkt. Wir reden Dinge schön, nennen sie aber nicht beim Name. Wir reden, ohne etwas zu sagen. Wir suchen Wortkreationen, die jeder Grammatik entbehren.

Dieses Buch ist wie ein Spiegel vor dem Gesicht. Die üblichen und gängigen Fehler werden schonungslos aufgedeckt und besprochen. Selbst Ausflüge in die Grammatik lesen sich gut. Der Autor wendet selbst alle Regeln an, die er vermittelt. Das ergibt ein stimmiges und empfehlenswertes Buch.

Kaufen und lesen – für mehr Klarheit.

Cookie-Einstellungen
Auf dieser Website werden Cookie verwendet. Diese werden für den Betrieb der Website benötigt oder helfen uns dabei, die Website zu verbessern.
Alle Cookies zulassen
Auswahl speichern
Individuelle Einstellungen
Individuelle Einstellungen
Dies ist eine Übersicht aller Cookies, die auf der Website verwendet werden. Sie haben die Möglichkeit, individuelle Cookie-Einstellungen vorzunehmen. Geben Sie einzelnen Cookies oder ganzen Gruppen Ihre Einwilligung. Essentielle Cookies lassen sich nicht deaktivieren.
Speichern
Abbrechen
Essenziell (1)
Essenzielle Cookies werden für die grundlegende Funktionalität der Website benötigt.
Cookies anzeigen