Show / Hide Table of Contents

    REST Endpoint für Service-Methoden

    Standardmäßig bietet ein Service Host den ausgewählten Service bzw. dessen Service Contract nach außen hin über WCF und damit das SOAP-Protokoll an. Moderne Applikationen und allen voran Webapplikationen in Borwsern oder auf mobilen Endgeräten unterstützen mittlerweile jedoch oft kein SOAP mehr und bevorzugen deshalb die Kommunikation über JSON.

    Framework Studio bietet die Möglichkeit, den Service Host so zu konfigurieren, dass vollständig automatisch ein zusätzlicher Endpoint generiert wird, mit dem die im Service Contract propagierten Methoden auch über JSON und damit über einen normalen HTTP-Request angesprochen werden können.

    Um die vollständige Automatisierung dieser Funktionalität gewährleisten zu können, bedarf es einiger Regeln, die beachtet werden müssen. Diese betreffen vor allem die Kommunikation zwischen dem Client und dem generierten REST Endpoint des Service Hosts.

    Aktivieren des REST-Endpoints

    Der Service Host generiert den REST Endpoint, wenn die Checkbox Generate REST Endpoint aktiviert wird.

    Service Host REST-Endpoint

    In der Konfigurationsdatei des Servic Hosts wird dadurch ein neuer Endpoint mit dem ausgewählten Contract registriert:

    <endpoint name="rest" address="api" binding="webHttpBinding" contract="App.IHelloServiceContract" />
    
    Warning

    Der REST Endpoint basiert auf der in WCF integrierten webHttpBinding. In einem WCF-Service darf für einen Service Contract nur ein Binding eines bestimmten Typs registriert werden. Sollte am Service Host schon ein Endpoint mit einem webHttpBinding für den ausgewählten Contract existieren, so kann das automatische Generieren des REST Endpoints nicht verwendet werden bzw. wird beim Start des Service Host zu Fehlern führen.

    Der Endpoint hat fest den Namen api und wird immer auf der im Service Host angegebenen Base Address registiert.

    Ist die Base Address z.B.

    http://localhost:1234/HelloServiceHost

    dann wird der REST Endpoint unter

    http://localhost:1234/HelloServiceHost/api

    zu finden sein.

    Im Development Service ist der neue Endpoint auch in der Auflistung im System Tray zu sehen:

    DevService Endpoints

    Aufrufen von Service-Methoden am REST Endpoint

    Das Aufrufen einer durch den REST Endpoint bereitgestellten Service Methode erfolgt über einen normalen HTTP-Request. Dabei gibt es einige Regeln, die beachtet werden müssen. Nachfolgend werden alle Spezialitäten der Kommunikation detailliert beschrieben und mit mehreren Beispielen transparent dargestellt.

    In den Beispielen wird ein Service namens "HelloService" verwendet. Dieser besteht aus 4 Methoden, die den Benutzer auf unterschiedliche Weise grüßen.

    Service Host Methoden

    Separate Urls für Service-Methoden

    Um die Methoden am REST Endpoint voneinander unterscheiden zu können, besitzt jede Methode eine eigene Url. Ist der REST Endpoint beispielsweise unter

    http://localhost:1234/HelloServiceHost/api

    registriert, so werden die einzelnen Methoden wie folgt angesprochen:

    Methodenname Url
    SayHello http://localhost:1234/HelloServiceHost/api/sayhello
    SayHelloComplex http://localhost:1234/HelloServiceHost/api/sayhellocomplex
    SayHelloFault http://localhost:1234/HelloServiceHost/api/sayhellofault
    SayHelloName http://localhost:1234/HelloServiceHost/api/sayhelloname

    Der Methodenname wird also als Url-Erweiterung am REST Endpoint genutzt. Auf Groß- und Kleinschreibung muss an dieser Stelle nicht geachtet werden.

    Um testweise HTTP-Requests auf den Service Host absetzen zu können, wird in den nachfolgenden Beispielen die App Postman genutzt. Sie ist kostenlos unter https://www.postman.com erhältlich.

    Postman

    Beispiel 1: Parameterlose Methoden

    public virtual string SayHello()
    {
        return "Hello!";
    }
    

    Am REST Endpoint ist diese Methode unter http://localhost:1234/HelloServiceHost/api/sayhello aufrufbar.

    Parameterlose Methoden können ausschließlich mit HTTP GET aufgerufen werden.

    Die Header Content-Type und Content-Encoding müssen nicht zwingend gesetzt sein, da bei parameterlosen Methoden im Request kein HTTP-Content übertragen wird.

    Der Header Accept muss auf application/json gesetzt sein oder diesen MIME-Type beinhalten (wird im Beispiel durch */* abgedeckt), ansonsten führt der Response am Client zu einem Fehler.

    Beispiel 1

    Der Response hat ausschließlich den Content-Type application/json und das Content-Encoding utf-8. Somit kann der Response ohne Umwege z.B. vom im JavaScript integrierten JSON-Parser eingelesen werden.

    Beispiel 2: Methoden mit Parametern (einfache Datentypen)

    public virtual string SayHelloName(string name)
    {
        return "Hello " + name + "!";
    }
    

    Am REST Endpoint ist diese Methode unter http://localhost:1234/HelloServiceHost/api/sayhelloname aufrufbar.

    Methoden mit Parametern müssen zwinged mit HTTP POST aufgerufen werden.

    Der Header Content-Type muss zwingend auf application/json gesetzt sein, da der Service Host eine JSON-Kommunikation erwartet.

    Um Encoding-Fehlern vorzubeugen, wird empfohlen, den Header Content-Encoding entsprechend des übertragenen Contents zu setzen (vorzugsweise utf-8).

    Der Header Accept muss auf application/json gesetzt sein oder diesen MIME-Type beinhalten (wird im Beispiel durch */* abgedeckt), ansonsten führt der Response am Client zu einem Fehler.

    Methodenparameter müssen im HTTP-Content in einem JSON-Objekt übertragen werden. Url-Parameter werden nicht unterstützt. Jeder Methodenparameter wird als JSON-Property mit dem exakten Namen (Groß- und Kleinschreibung beatchen) übergeben.

    Im Beispiel wäre das der Parameter string name. Der Parameter wird im JSON wie folgt übertragen:

    {
        "name": "Max Mustermann"
    }
    

    Beispiel 2

    Der Response hat ausschließlich den Content-Type application/json und das Content-Encoding utf-8. Somit kann der Response ohne Umwege z.B. vom im JavaScript integrierten JSON-Parser eingelesen werden.

    Hat eine Methode mehrere Parameter, so werden diese im JSON-Objekt ebenfalls als Properties hinzugefügt. Die Reihenfolge ist dabei nicht relevant.

    public virtual string SayHelloMultiple(string name, int age, string city)
    

    Diese Methode würde folgendes JSON-Objekt wie gewünscht verarbeiten, obwohl die Parameter vertauscht sind:

    {
        "name": "Max Mustermann",
        "city": "Stockach",
        "age": 36
    }
    

    Beispiel 3: Methoden mit Parametern (komplexe Datentypen)

    public virtual dcHelloResult SayHelloComplex(dcHelloInput input)
    {
        dcHelloResult result = new dcHelloResult()
        {
            sGreeting = "Welcome!",
            sFirstName = input.sFirstName,
            sLastName = input.sLastName
        };
    
        return result;
    }
    

    Für einen Methodenaufruf mit komplexen Datentypen wie z.B. DataContracts oder Arrays gelten dieselben Regeln wie für einen Methodenaufruf mit einfachen Datentypen. Der einzige Unterschied ist, dass der jeweilige Parameter als JSON-Objekt übertragen wird.

    Im Beispiel hat die Methode einen Parameter vom Typ dcHelloInput mit zwei Properties. Dieser DataContract sieht im Framework Studio folgendermaßen aus:

    dcHelloInput

    Als Rückgabetyp dient der DataContract dcHelloResult mit drei Properties, welcher so definiert ist:

    dcHelloResult

    Der Aufruf der Service-Methode gestaltet sich intuitiv wie folgt:

    Beispiel 3

    Hat eine Methode mehrere komplexe Parameter, so gilt dieselbe Nomenklatur wie in Beispiel 2.

    public virtual dcHelloResult SayHelloMultiple(dcHellopInput input, dcHelloInput inputOther, string greeting)
    

    Diese Methode würde folgendes JSON-Objekt wie gewünscht verarbeiten:

    {
        "input": {
            "sFirstName": "Max",
            "sLastName": "Mustermann"
        },
        "inputOther": {
            "sFirstName": "Peter",
            "sLastName": "Pan"
        },
        "greeting": "Hi"
    }
    

    Beispiel 4: Exceptions und FaultContracts

    public virtual void SayHelloFault()
    {
        dcFault fault = new dcFault()
        {
            sProp1 = "Fault Property 1",
            sProp2 = "Fault Property 2"
        };
    
        throw new FaultException<dcFault>(fault, "OH NO!", new FaultCode("Custom Fault Code"), "Custom Fault Action");
    }
    

    Tritt währende der Verarbeitung im Service eine Exception auf oder wird aus der Logik heraus eine FaultException geworfen, so werden auch diese im JSON an den Client übertragen. Die FaulException aus obigem Code stellt sich im JSON folgendermaßen dar:

    {
        "Action": "Custom Fault Action",
        "Code": {
            "IsPredefinedFault": true,
            "IsSenderFault": false,
            "IsReceiverFault": false,
            "Namespace": "",
            "Name": "Custom Fault Code",
            "SubCode": null
        },
        "Reason": {
            "Translations": [
                {
                    "XmlLang": "en-US",
                    "Text": "OH NO!"
                }
            ]
        },
        "Detail": {
            "sProp2": "Fault Property 2",
            "sProp1": "Fault Property 1"
        },
        "Exception": {
            "ClassName": "System.ServiceModel.FaultException`1[App.dcFault]",
            "Message": "OH NO!",
            "Data": {},
            "InnerException": null,
            "HelpURL": null,
            "StackTraceString": "   at App.REST_HelloService.SayHelloFault()\r\n   at SyncInvokeSayHelloFault(Object , Object[] , Object[] )\r\n   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)\r\n   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\r\n   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\r\n   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)",
            "RemoteStackTraceString": null,
            "RemoteStackIndex": 0,
            "ExceptionMethod": "8\nSayHelloFault\nApp_Svc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\nApp.REST_HelloService\nVoid SayHelloFault()",
            "HResult": -2146233087,
            "Source": "App_Svc",
            "WatsonBuckets": null,
            "code": [
                {}
            ],
            "reason": [
                {}
            ],
            "messageFault": null,
            "action": "Custom Fault Action",
            "detail": {
                "sProp2": "Fault Property 2",
                "sProp1": "Fault Property 1"
            }
        },
        "Message": "OH NO!",
        "StackTrace": "   at App.REST_HelloService.SayHelloFault()\r\n   at SyncInvokeSayHelloFault(Object , Object[] , Object[] )\r\n   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)\r\n   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\r\n   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\r\n   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)"
    }
    

    Folgende Properties stehen zur Verfügung, um am Client ggf. eine adequate Fehlermeldung anzeigen zu können:

    Property Typ Beschreibung
    Action String Beinhaltet die angegebene FaulAction
    Code Object Beinhaltet den angegebenen FaulCode inkl. aller SubCodes
    Reason Object Beinhaltet den angegebenen FaulReason, ggf. mit allen übergebenen Übersetzungen
    Detail User Defined Beinhaltet das Objekt, welches vom Entwickler als Fault in die FaultException gegeben wurde. Im Beispiel die Variable fault vom Typ dcFault
    Exception Object Beinhaltet die in Gänze serialisierte Exception mit allen Properties
    Message String Beinhaltet das Message-Property der Exception
    StackTrace String Beinhaltet das StackTraceString-Property der Exception

    JSON Format

    JSON kann verschiedene Datentypen in unterschiedlichsten Formaten übertragen. Für eine Datums- und Zeitangabe gibt es im JSON z.B. keinerlei Spezifikation, wie diese Information dargestellt werden soll. Da es im JSON keinen dedizierten DateTime-Typ gibt, wird ein Datum meist als String repräsentiert. Wie genau dieser String letztenendes aussieht, hängt vom JSON-Serializer ab.

    Der in WCF integrierte DataContractJsonSerializer von Microsoft überträgt den .NET-Typ DateTime z.B. so:

    {
        "datetime": "/Date(1335205592410-0500)/"
    }
    

    JavaScript serialisiert ein Datum aber so:

    {
        "datetime": "2012-04-23T18:25:43.511Z"
    }
    

    Der Effekt ist, dass sich ggf. Server und Client nicht verstehen und letztenendes auf Fehler laufen, da sie einen unterschiedlichen "JSON-Dialekt" sprechen. Aus diesem Grund arbeitet der REST Endpoint in einem Framework Studio Service Host mit dem bekannten JSON.NET Serializer, der weltweit in millionen von Anwendungen zum Einsatz kommt. Das von diesem Serializer ausgegebene JSON ist kompatibel mit JavaScript und versteht im Umkehrschluss auch die meisten Dialekte, ohne dass der Entwickler selbst in den Serialisierungsprozess eingreifen muss.

    ref- und out-Parameter

    WCF unterstützt generell ref- und out-Parameter in Service Methoden. Dies mündet in der SOAP-Kommunikation letztenendes in mehreren Rückabewerten einer Methode. Der REST Endpoint unterstützt ref- und out-Parameter NICHT. Wenn mehr als ein Rückgabeparameter in einer Service-Methode nötig sind, wird empfohlen, einen DataContract für diesen Zweck zu erstellen und diesen als Rückgabetypen für die Methode zu verwenden (siehe Beispiel 3).

    Back to top Generated by DocFX