Custom Message Parsing in SAPUI5
This post will walk you through creating a custom MessageParser using a sample scenario for an example in which it could be used. The entire code for the working application created in this post can be found here: pritin-tyagaraj/ui5-message-parser · GitHub
One more important thing it does, is take care of handling messages (success/error/information) returned by the server and convert them into appropriate UI5 objects which can then be used by your application (for example, by an sap.m.MessagePopover control instance). It is able to do this without you writing any application code, because it knows (thanks to the OData protocol that is being followed) how exactly the server’s response will be structured and where within it to look for messages.
If you’ve built an application that uses the ODataModel – even without code to explicitly handle messages – you can still already see the messages that the framework is automagically creating by executing the following snippet in your console.
But what happens when you are not using an OData service? What if it’s a HANA XS service or some other service that does not follow the OData protocol? Here is where things get interesting – the framework is unable to understand the server’s response because it does not follow a protocol that UI5 understands.
Enter, stage left – sap.ui.core.message.MessageParser
To solve this problem in a neat way, we must implement the MessageParser interface that the framework provides. This lets us write code to specify how to extract messages from the server’s response. Before jumping into sample code however, let’s understand the different entities that are involved in getting custom message parsing to work in our application. The following diagram gives a high level overview of how the various pieces fit together. As we continue reading about the roles that each of these boxes plays, the diagram will become easier to comprehend.
|This is a type of model (just like JSONModel, XMLModel etc. are types) which stores, by no surprise, messages. By ‘messages’ here, I mean instances of the sap.ui.core.message.Message class. These have properties like ‘id’, ‘type’, ‘description’ and ‘descriptionUrl’ that are typically used to describe each message.|
|An instance of this class gets to know (indirectly from our message parser) which messages are no longer relevant (need to be removed from the MessageModel) and which messages are new (need to be added to the MessageModel). That’s pretty much the only role that the MessageManager is playing in this picture – to make sure the messages within the MessageModel are up to date.|
A MessageParser is a static class that implements the sap.ui.core.message.MessageParser interface. The only two methods that must mandatorily be implemented are parse() and setProcessor().
The parse() method expects one argument – the server’s response. Our implementation of this method is expected to extract the messages within the response, and then decide which messages are ‘new’ and which ones are ‘old’ (The MessageManager uses this, like we saw above)
This is the class that is actually processing the server’s response (and as a result, processing (or ‘receiving’) the messages from the server). If your application uses an ODataModel, your ODataModel is playing the role of Message Processor, since it is responsible for talking to the OData service and sending/receiving information.
The ODataModel uses a standard implementation of the MessageParser interface (sap.ui.model.odata.ODataMessageParser); and this is how messages from the OData service got pushed into the core MessageModel without us writing any code.
To demonstrate how exactly our CustomMessageParser will fit into the equation though, we’ll be creating our own model. Since we will be interacting with the non-OData service using this custom model, it plays the role of MessageProcessor.
Our sample application
Defining the protocol
So we’ve decided to not use the OData protocol, but we still need to stick to some protocol! Let’s make up a protocol (as is common to do within non-OData projects) and decide that the server’s response must always have a structure similar to the following:
So our protocol requires any response from our server to be a JSON object with a ‘data’ node in the response, and a separate node called ‘messages’, which should be an array of objects – each object representing one message.
Creating the CustomMessageParser
Since we have now defined a protocol for our application to follow, we can already write our own implementation of a MessageParser.
This parse() method is called from our custom model (we’ll see this next), and gets the server’s response as an argument. Since we know that the response will have a node called ‘messages’ within it, we loop through it and create instances of sap.ui.core.message.Message (Step #2 in the overview diagram).
Once we have an array of all sap.ui.core.message.Message instances, we fire the messageChange event (Step #3) to which the standard MessageManager class will react and in response keep its MessageModel in sync. The MessageManager is instantiated in our view’s controller, and our sap.m.MessagePopover control will be bound to the MessageManager‘s model. We will see both of these towards the end of this post.
Creating a custom model
We call the parse() method of our very own MessageParser from within the ‘Message Processor’, which as we saw a while back is the entity that is actually making the network request to fetch data. Since the ODataModel already ships with a powerful message parser, let’s extend the JSONModel with a ‘read’ method similar to the ODataModel‘s to demonstrate where the call MessageParser is plugged in.
Once our method fetches data from the server (in our example we just fetch it from a file), it passes the response to the parse() method of the MessageParser (Step #1) to extract any messages that the response may contain. Then, it goes on to update its own internal structures to contain the actual data that the server returned. This can then be bound to UI controls via data binding.
In the view’s controller, we trigger the read() method of our custom model to trigger (OK, “simulate”) a network operation which retrieves a JSON object from the server. The method is expected to place the received data in the ‘/Customers’ path; to which the sap.m.List control in our view will be bound.
Apart from triggering the read, we also create an instance of the standard sap.ui.core.message.MessageManager class. Once we register our custom model (a.k.a “the message processor”) with this MessageManager instance, it will react to any messageChange events that our parser fires on the model (Step #4)
Showing messages on the UI
With everything we’ve looked at so far, we now have a MessageManager which contains a MessageModel – which in turn contains all the messages that we’ve received from our server. We now use a suitable control which can be bound (via a ListBinding) to the MessageModel. The sap.m library has a control specifically built for showing messages, so we use it – the sap.m.MessagePopover control.
What we get will this work (which is by no means “just a few lines of code”) and this exact output could have been achieved with much fewer lines of code. However, this is a neat and more importantly “scalable” way to handle messages when using protocols other than OData in your project – in the sense that even in a project which has a complex protocol being followed, this approach will work well and the developed code will be easy to maintain.
Hope you enjoyed reading this writeup! The source code for a working example can be found at pritin-tyagaraj/ui5-message-parser · GitHub. Your feedback is most welcome in the comments section below. 🙂
An interesting read Pritin. Thanks for your insight.
Good post Pritin. Thank you for that.
I have a few questions,
1. Is the MessageParser a new feature that was released some time back?
2. If its a new feature, how was this scenario handled in the past?
3. In Background, you mention that the framework does a lot of tasks that the application would otherwise have to do. Which framework does this? UI5 framework I suppose?
4. The code in the defining the protocol is shown as an image. It is hard for me to read this without clicking on it and opening a new popup. Can a larger image be provided for this (by wrapping the long text to 2 or more lines) or this be provided by normal code snippet like how you have given in Background section?
Hey Harish, glad you enjoyed reading it. To answer your questions...
1. Yes correct, this was introduced recently (though I'm not sure with which version exactly)
3. Yes correct. The UI5 framework does a lot of such things to make application development simpler (like any good framework should do 🙂 )
4. Thanks for pointing this out! I will change this.
first of all, thank you for sharing this with us. Great documentation.
You explained the following:
"One more important thing it does, is take care of handling messages (success/error/information) returned by the server and convert them into appropriate UI5 objects which can then be used by your application (for example, "
Does it mean, from now I can send not only error/exception messages, but info and success messages using a standard way from the backend? In our system we are on level SAP_GWFND 7.4 sp11, but I found no SAP standard tool/way to fill the response with info and success mesages, which is then extracted and analyzed in UI5 the way you are describing above. The interface method parameters in the GateWay service builder does not let me to fill the response with info/warning and success messages. I can raise an exception, and fill the exception message container with error messages only. Do you know, how-to-fill the response with success messages using a standard way/tool/helper class in teh backend? Sofar I was filling the response with my custom messages and format, and extracted it manually in my UI5 application from the response. As you describe, now it is very easy to get the messages from the response, now I would like to fill it in the backend in the right format, but don't know how .
Do you have any info on this, or suggestion, where to start searching for that ABAP backend feature to fill the response properly?
Thank you, this documentation is describing the most wanted stuff nowadays in UI5 🙂
Hey Attila... Glad you found the post useful! Yes, you can pass info/success messages along with the response from Gateway. If the response only has the message (say, an error because of which no records can be returned), the message is part of the response body. If messages are returned along with data (like in the example in this post), the messages are present in a response header (not body) called 'sap-message'. I checked with my colleague,Harish R, and he said he could help you out here a bit more from the GW technical perspective. Harish? 🙂
Hello... I don't understand your question fully. Can I know the scenario in which you would like to return a success or information message? Is it during a create, update or delete or is it for a function import in SAP Netweaver Gateway OData? Or is it from something else like HANA XS Odata?
I'm implementing ABAP GW OData services, and sending success/info/warning messages in CRUD operations and Function Imports. As I see in this article, there is a support to extract on the UI side,I'm wondering whether developers are supported in ABAP to fill the requests easily and manually with (info/success/warning) messages, without raising an exception. I did this work manually (like described here).
I'm interested whether a helper class or sg like this exists in the backend to do that in the proper format, so that it can be extracted the way it is described in this blog post by Pritin. If there is no such feature in the backend at the moment, then what is the right format I need to use when filling the response.
The above post shows how to handle messages from non SAP Netweaver Gateway services.
In case of SAP NW GW OData services, there is a standard way to add I/S/W messages without raising an exception. In the DPC method where you wish to add the message(s), call the method mo_context->get_message_container( )->add_message( ). Pass the necessary parameters for the message(s) and set iv_add_to_response_header as abap_true.
DATA: lo_msg_container TYPE REF TO /iwbep/if_message_container. lo_msg_container = mo_context->get_message_container( ). lo_msg_container->add_message(
iv_msg_type = 'I'
iv_msg_id = 'ZTEST'
iv_msg_number = 001
iv_add_to_response_header = abap_true ).
In the request response header, you can find a new field called sap-message with the message(s) that you had passed above. This the standard way of passing messages back with the response without raising exceptions.
Hope this helps.
Now, whether UI5 framework automatically reads the messages passed in sap-message and shows on the screen or some manual parsing is required is something Pritin Tyagaraj can say.
Thank you, this is exactly what I need 🙂 .