Ui ui ui ui ui – ErrorHandling in SAPUI5/Fiori Apps
Kennen Sie das? Sie entwickeln eine eigene SAPUI5/Fiori App und im Backend tritt ein Fehler auf. Falls Sie diesen nicht behandeln und propagieren, erhalten Sie in Ihrer Anwendung oft dieses Bild: Wenn Sie wissen möchten, wie Sie auf die unterschiedlichsten Dinge im Backend reagieren können, schauen Sie sich diesen Beitrag an. Hier erkläre ich Ihnen, welche Möglichkeiten Sie haben, das ErrorHandling im Backend anzugehen und Fehler als hilfreiche Meldung ans Frontend zu propagieren.
Vorbereitung des Frontends
Zunächst benötigen wir frondendseitig die Möglichkeit, mit Fehlermeldungen umzugehen, die aus dem Backend kommen. Dafür wird in einigen Templates die ErrorHandler.js gleich mitgeliefert. Sollten Sie diese in Ihrer aktuellen Anwendung nicht eingebunden haben, können Sie diese einfach aus diesem Beitrag kopieren und einfügen. Achten Sie dabei dann unbedingt darauf, die ID Ihrer App an der entsprechenden Stelle zu ersetzen.
ErrorHandler.js
sap.ui.define([
"sap/ui/base/Object",
"sap/m/MessageBox"
], function (UI5Object, MessageBox) {
"use strict";
return UI5Object.extend(“ID_IHRER_APP.controller.ErrorHandler”, {
/**
* Handles application errors by automatically attaching to the model events and displaying errors when needed.
* @class
* @param {sap.ui.core.UIComponent} oComponent reference to the app’s component
* @public
* @alias sap.Test123.controller.ErrorHandler
*/
constructor : function (oComponent) {
this._oResourceBundle = oComponent.getModel(“i18n”).getResourceBundle();
this._oComponent = oComponent;
this._oModel = oComponent.getModel();
this._bMessageOpen = false;
this._sErrorText = this._oResourceBundle.getText(“errorText”);
this._oModel.attachMetadataFailed(function (oEvent) {
var oParams = oEvent.getParameters();
this._showServiceError(oParams.response);
}, this);
this._oModel.attachRequestFailed(function (oEvent) {
var oParams = oEvent.getParameters();
// An entity that was not found in the service is also throwing a 404 error in oData.
// We already cover this case with a notFound target so we skip it here.
// A request that cannot be sent to the server is a technical error that we have to handle though
if (oParams.response.statusCode !== “404” || (oParams.response.statusCode === 404 && oParams.response.responseText.indexOf(“Cannot POST”) === 0)) {
this._showServiceError(oParams.response);
}
}, this);
},
/**
* Shows a {@link sap.m.MessageBox} when a service call has failed.
* Only the first error message will be display.
* @param {string} sDetails a technical error to be displayed on request
* @private
*/
_showServiceError : function (sDetails) {
if (this._bMessageOpen) {
return;
}
this._sErrorText = JSON.parse(sDetails.responseText).error.message.value;
this._bMessageOpen = true;
MessageBox.error(
this._sErrorText,
{
id : “serviceErrorMessageBox”,
//details : sDetails,
styleClass : this._oComponent.getContentDensityClass(),
actions : [MessageBox.Action.CLOSE],
onClose : function () {
this._bMessageOpen = false;
}.bind(this)
}
);
}
});
}
);
Ein weiterer wichtiger Schritt ist, den ErrorHandler in der Component.js zu registrieren (auch hier achten Sie bitte auf die ID Ihrer Anwendung):
Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"ID_IHRER_APP/model/models",
"ID_IHRER_APP/controller/ListSelector",
"ID_IHRER_APP/controller/ErrorHandler"
], function (UIComponent, Device, models, ListSelector, ErrorHandler) {
"use strict";
return UIComponent.extend(“ID_IHRER_APP.Component”, {
metadata : {
manifest : “json”
},
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* In this method, the FLP and device models are set and the router is initialized.
* @public
* @override
*/
init : function () {
this.oListSelector = new ListSelector();
this._oErrorHandler = new ErrorHandler(this);
// set the device model
this.setModel(models.createDeviceModel(), “device”);
// set the FLP model
this.setModel(models.createFLPModel(), “FLP”);
// call the base component’s init function and create the App view
UIComponent.prototype.init.apply(this, arguments);
// create the views based on the url/hash
this.getRouter().initialize();
},
/**
* The component is destroyed by UI5 automatically.
* In this method, the ListSelector and ErrorHandler are destroyed.
* @public
* @override
*/
destroy : function () {
this.oListSelector.destroy();
this._oErrorHandler.destroy();
// call the base component’s destroy function
UIComponent.prototype.destroy.apply(this, arguments);
},
/**
* This method can be called to determine whether the sapUiSizeCompact or sapUiSizeCozy
* design mode class should be set, which influences the size appearance of some controls.
* @public
* @return {string} css class, either ‘sapUiSizeCompact’ or ‘sapUiSizeCozy’ – or an empty string if no css class should be set
*/
getContentDensityClass : function() {
if (this._sContentDensityClass === undefined) {
// check whether FLP has already set the content density class; do nothing in this case
if (jQuery(document.body).hasClass(“sapUiSizeCozy”) || jQuery(document.body).hasClass(“sapUiSizeCompact”)) {
this._sContentDensityClass = “”;
} else if (!Device.support.touch) { // apply “compact” mode if touch is not supported
this._sContentDensityClass = “sapUiSizeCompact”;
} else {
// “cozy” in case of touch support; default for most sap.m controls, but needed for desktop-first controls like sap.ui.table.Table
this._sContentDensityClass = “sapUiSizeCozy”;
}
}
return this._sContentDensityClass;
}
});
}
);
Mehr müssen wir nun in der Anwendung erst einmal nicht tun. Wir können uns nun also dem Backend widmen.
Vorbereitung des Backends
Zunächst empfiehlt es sich eine Ausnahmeklasse anzulegen. In dieser können wir dann verschiedene Attribute, Interfaces, Texte und Methoden definieren bzw. angeben.
Als Attribute können wir hier beispielsweise unsere Ausnahmetexte angeben.
Wichtig sind die abgebildeten Interfaces. Diese benötigen wir innerhalb der Ausnahmeklasse.
Zu den Texten, die wir ebenfalls bereits als Attribute gepflegt haben, können wir im Bereich “Texte” jeweils einen sprechenden Langtext definieren.
Neben den Methoden aus den Interfaces und dem Constructor habe ich hier noch eine weitere Methode “Raise” angelegt. In dieser wird die entsprechende Exception geworfen. Zwingend benötigt ist diese jedoch nicht, bietet sich allerdings im Sinne der Kapselung an.
Wir starten unsere Reise der Fehlerbehandlung in diesem Szenario in der Methode “Header_Entityset_Get_Entity” (also einer GET-Methode eines OData-Services). Hier haben wir die folgenden Ausnahmen hinzugefügt:
Die eigentliche Behandlung der Fehlermeldungen kann über die sogenannten Try-Catch-Blöcke stattfinden. Diese befinden sich in unserem Fall direkt innerhalb der genannten Methode. Wichtig: In jeder Methode, die eine Exception werfen kann, muss auch die Ausnahme angegeben sein, wenn diese propagiert werden soll. Gleiches gilt auch für “weiterreichende” Methoden. Durch das Raise Exception in der “untersten” Methode und die hinzugefügte Ausnahme in allen Methoden wird die Exception weitergereicht, bis sie behandelt wird. Beispielhaft dargestellt wird dies in den folgenden Abbildungen:
Hier wird eine Methode innerhalb des Try-Blocks aufgerufen, die unter Umständen eine Exception raised. Sollte dies der Fall sein, wird der Catch-Block abgehandelt. Innerhalb dieses Blocks wird die Methode “Handle_Status_Error” aufgerufen. Diese sieht wie folgt aus:
Wir sehen, dass in dieser Methode die Ausnahme “/IWBEP/CX_MGW_BUSI_EXCEPTION” angegeben wurde.
Alternativ zur Behandlung in einer weiteren Methode, können Sie natürlich die Fehlerbehandlung direkt im Catch-Block vornehmen:
In diesem Fall wird der Funktionsbaustein ADD_MESSAGE_TEXT_ONLY genutzt, um den Fehlertext zu ermitteln. Wir sollten beachten, dass Code, der sich nach dem Try-Catch-Block befindet ausgeführt wird, solange hier keine weitere Exception geraised wird. Dies ist in vielen Fällen sinnvoll, manchmal aber auch hinderlich.
Schon am Anfang das Ende im Sinn haben
Sie sehen also, dass vom Werfen einer Ausnahme bis zur Anzeige einer sprechenden Fehlermeldung im Frontend einiges zu tun ist. Ich empfehle Ihnen, die Grundsteine für das ErrorHandling möglichst früh zu legen. Dann können Sie im weiteren Verlauf Ihrer Entwicklung immer wieder darauf zurückgreifen. Sollte Ihnen noch etwas unklar geblieben sein, zögern Sie bitte nicht, mich zu kontaktieren.
mindsquare Fiori Launchpad Newsfeed
Der mindsquare Fiori Launchpad Newsfeed hilft Ihnen dabei Ihre Mitarbeiter auf dem Laufenden zu halten und mit wichtigen Informationen zu versorgen.