In einem früheren Blog haben wir uns mit der Fehlerbehandlung von einer API beschäftigt, bei der der Endpunkt nicht verfügbar ist. Zur Erinnerung: in der vollständigen Sequenz der API haben wir zwei Elemente hinzugefügt, eines, das den Fehlercode und eine Fehlermeldung auf der Konsole anzeigt, und die anderen Elemente haben eine benutzerdefinierte Meldung erstellt und den Fehlercode auf einen HTTP 404 (nicht gefunden) gesetzt. Wir werden in diesem Blog WireMock verwenden, um einen Endpunkt zu erstellen.
Wir verwenden die Fehlersequenz deshalb, weil dies die Sequenz ist, die ausgelöst wird, wenn etwas schief läuft. Aber es gibt nicht nur, wenn der Endpunkt nicht gefunden werden kann, die Fehlersequenz ausgelöst wird, sondern auch andere Situationen, die die Fehlersequenz auslösen. Ein Beispiel dafür ist ein Endpunkt, der nicht innerhalb der Zeit antwortet. Um das zu testen, werden wir mit WireMock einen Endpunkt erstellen, der immer einige Sekunden später antwortet als der Timeout, den wir für den Endpunkt festgelegt haben.
Einen Endpunkt definieren
Wenn Sie einen Endpunkt definieren, gibt es mehr als nur die URL oder Adresse. Bei der Definition des Endpunkts handelt es sich nur um die URI-Vorlage und die Methoden (im Falle eines HTTP-Endpunkts), aber wenn Sie näher heranzoomen, können Sie erkennen, dass es eine Endpunkt-Fehlerbehandlung gibt, die Sie mit Dingen wie Timeouts, Suspend-Codes und so weiter konfigurieren können.
Wir werden die Endpunktkonfiguration vorerst auslassen und einfach einen Endpunkt wie oben gezeigt erstellen. In WireMock definieren wir einen neuen Service mit dem Namen servicedelay.json und legen ihn wiederum in das Mapping-Verzeichnis von WireMock.
{
"request": {
"method": "GET",
"url": "/yenlo/api/testdelay"
},
"response": {
"status": 200,
"fixedDelayMilliseconds": 31000,
"body": "Service is respondingn"
}
}
Wir prüfen den Endpunkt mit SoapUI und erhalten nach 31 Sekunden eine Antwort, die dem vorherigen Dienst sehr ähnlich ist. Wir erstellen also eine neue API, die diesen spezifischen Endpunkt aufruft, und sehen, wie die Antwort ausfallen wird. Wir werden eine wortgetreue Kopie der vorherigen API erstellen und nur den Endpunkt ändern und umbenennen, mehr nicht.
Ergänzen Sie die API in der Composite Application und stellen Sie die Autodatei für den Enterprise Integrator bereit, der natürlich laufen sollte.
Kontrollieren Sie, ob die API mithilfe der Verwaltungsoberfläche auf dem Unternehmensintegrator bereitgestellt wird.
Testen Sie die API mithilfe der Soap UI mit der API-Aufruf-URL, die Sie in der Übersicht der bereitgestellten APIs sehen können. Tatsächlich erhalten wir nach 31 Sekunden eine Antwort vom Dienst, einen 200-Code und die Meldung.
Jetzt müssen wir die Konfiguration des Endpunkts so ändern, dass er tatsächlich einen Timeout auslöst, wenn nicht innerhalb eines bestimmten Zeitrahmens eine Antwort erfolgt. Dafür müssen wir zwei wichtige Konfigurationen definieren: den Timeout-Status des Endpunkts und die Timeout-Konfiguration.
Wenn keine Antwort kommt, werden wir dem Enterprise Integrator sagen, dass er dreimal versuchen soll, eine Verbindung herzustellen, wobei zwischen jedem Wiederholungsversuch eine Verzögerung von 500 ms liegt. Ehe wir zur Unterbrechung übergehen, haben wir eine Dauer von 10.000 ms sowie die Ausführung der gesamten Sequenz im Falle einer Unterbrechung.
Aus Sicht des Quellcodes sieht es ungefähr so aus (wir haben hier nur die Endpunktkonfiguration gezeigt).
<send>
<endpoint>
<http method="get" uri-template="http://localhost:8080/yenlo/api/testdelay">
<timeout>
<duration>10000</duration>
<responseAction>fault</responseAction>
</timeout>
<markForSuspension>
<retriesBeforeSuspension>3</retriesBeforeSuspension>
<retryDelay>500</retryDelay>
</markForSuspension>
</http>
<description/>
</endpoint>
</send>
Wenn wir die API mit dieser Konfiguration aufrufen, wird sie eine Reihe von Versuchen durchführen und dann die Fehlersequenz auslösen.
[2019-05-10 08:50:22,758] [EI-Core] WARN - EndpointContext Endpoint : AnonymousEndpoint with address http://localhost:8080/yenlo/api/testdelay is marked as TIMEOUT and will be retried : 2 more time/s after : Fri May 10 08:50:23 CEST 2019 until its marked SUSPENDED for failure
[2019-05-10 08:50:22,759] [EI-Core] INFO - LogMediator Error = 101504, MSG = Send timeout, Trace = null, exception = null
[2019-05-10 08:50:22,773] [EI-Core] WARN - TimeoutHandler Expiring message ID : urn:uuid:073c955c-a893-4cf3-b0fd-2f8c076c3e27; dropping message after ENDPOINT_TIMEOUT of : 10 seconds for AnonymousEndpoint, URI : http://localhost:8080/yenlo/api/testdelay, Received through API : EPTest2
[2019-05-10 08:50:30,822] [EI-Core] WARN - SynapseCallbackReceiver Synapse received a response for the request with message Id : urn:uuid:073c955c-a893-4cf3-b0fd-2f8c076c3e27 But a callback is not registered (anymore) to process this response
Wie Sie sehen können, wird die gesamte Sequenz ausgelöst und der Fehlercode 101504 wird angezeigt. Wenn wir uns diesen Code ansehen, werden wir feststellen, dass er Folgendes bedeutet: „Connection timed out (no input was detected on this connection over the maximum period of inactivity)“.
Wir haben den Ablauf des Fehlers für eine Situation programmiert, nämlich einen 404 not found. Aber wie wir jetzt sehen, wird die gesamte Sequenz auch ausgelöst, wenn wir einen Timeout haben.
HTTP/1.1 404 Not Found
Host: localhost:8280
Accept-Encoding: gzip,deflate
Content-Type: application/json; charset=UTF-8
Date: Fri, 10 May 2019 06:50:22 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive
{ 'Error':'Connection cannot be made',
'Error Code' : 101504,
'Error Message' : ,
'Error Detail' : Send timeout}
Natürlich besteht die Lösung darin, sich den Fehlercode anzusehen und dann die richtige Fehlermeldung zu bestimmen, die die Antwort begleitet.
Switch Mediator
Am besten geht das mit einem Switch Mediator in der Fehlersequenz. Der Switch Mediator ermöglicht es Ihnen, basierend auf dem Fehlercode zu einem bestimmten Abschnitt zu verzweigen, um die Antwort zu erstellen sowie den Statuscode zu setzen.
Also, lassen Sie uns die Änderungen in der Fehlersequenz vornehmen. Der Switch Mediator arbeitet mit Fällen, die einem Fehlercode entsprechen und hat auch einen Standardfall, der ausgelöst wird, wenn keiner der anderen Fälle passt. Das ist eine einfache Möglichkeit, einen allgemeinen Fehler zu definieren, ohne dass alle Fehlercodes angegeben werden müssen. Wir beginnen damit, zwei Fälle zu definieren, einen für die Situation, dass der Endpunkt nicht gefunden werden kann, und einen für die Situation, dass wir einen Fehler beim Aufhängen erhalten.
Wir werden in jede der Fallanweisungen eine vollständige Konfiguration kopieren sowie einen generischen Fehler für den Standardfall erstellen.
Das führt jedoch zu ziemlich viel doppeltem Code. Deshalb werden wir die Elemente, die in jedem der Fälle genau gleich sind, herausnehmen und sie außerhalb des Switch Mediators verschieben. Das bedeutet natürlich den Log Mediator, der den Fehlercode auf der Konsole anzeigt. Das spart eine beträchtliche Menge an Code, aber wir können noch mehr tun! Der HTTP_SC ist verfügbar, daher muss dieser innerhalb des Switch Mediators liegen. Die von uns selbst definierte „Fehlermeldung“ ist jetzt hartkodiert, aber wir können diese in eine Variable umwandeln und auch diese Variable innerhalb des Switch Mediators setzen. Nachdem wir alle Änderungen vorgenommen haben, ist dies der resultierende Code:
<faultSequence>
<log description="" level="custom">
<property expression="get-property('ERROR_CODE')" name="Error"/>
<property expression="get-property('ERROR_MESSAGE')" name="MSG"/>
<property expression="get-property('ERROR_DETAIL')" name="Trace"/>
<property expression="$ctx:ERROR_EXCEPTION" name="exception"/>
</log>
<switch description="" source="get-property('ERROR_CODE')">
<case regex="101503">
<property name="HTTP_SC" scope="axis2" type="STRING" value="404"/>
<property name="OUR_ERROR" scope="axis2" type="STRING" value="Error':'Connection cannot be made"/>
</case>
<case regex="101504">
<property name="HTTP_SC" scope="axis2" type="STRING" value="408"/>
<property name="OUR_ERROR" scope="axis2" type="STRING" value="Error':'Connection timed out into suspension"/>
</case>
<default>
<property name="HTTP_SC" scope="axis2" type="STRING" value="500"/>
<property name="OUR_ERROR" scope="axis2" type="STRING" value="Error': Generic fault handler, code not specified"/>
</default>
</switch>
<payloadFactory media-type="json">
<format>{ 'Error':$1,
'Error Code' : $2,
'Error Message' : $3,
'Error Detail' : $4}</format>
<args>
<arg evaluator="xml" expression="get-property('ERROR_CODE')"/>
<arg evaluator="xml" expression="get-property('ERROR_EXCEPTION')"/>
<arg evaluator="xml" expression="get-property('ERROR_MESSAGE')"/>
</args>
</payloadFactory>
<respond/>
</faultSequence>
Wir haben nun eine vielseitige Fehlersequenz, die zwei Fehler abfängt, einen 404, 408 und den generischen, den wir 500 genannt haben.
Beim Ausprobieren in der Soap UI erhalten wir diese Meldung.
Hoffentlich war dieser Blog hilfreich. Wenn Sie Fragen oder Feedback haben, hinterlassen Sie bitte einen Kommentar. Möchten Sie mehr über zum Beispiel WSO2 Enterprise Integrator oder WSO2 API Manager erfahren? Besuchen Sie eine unserer WSO2-Schulungen.