Building a CRUD Application with SAPUI5 and ICF REST/JSON Service – Part 2
This is the part 2 of 3 of this blog series. In this part we will see the implementation of the Create, Read, Update, Delete methods.
Click here if you did not read the Part 1.
Table of Contents
Part 1
Prerequisites
Overview
SAPLink Nuggets and Eclipse Project download
References
SAPUI5
SAPLink
SAPLink plugins (SCN Code Exchange)
CL_TREX_JSON_SERIALIZER bug fixes
Extending SAPUI5 JSON Model
Creating the ICF REST Service
Creating the custom handler class
Creating the ICF node
Retrieving the request method and the parameters passed in the URL
Part 2
Implementing the REST Service
Model and DDIC objects
Implementing the Create method (POST HTTP verb)
Implementing the Read method (HTTP GET verb)
Implementing the Update method (PUT HTTP verb)
Implementing the Delete method (DELETE HTTP verb)
Part 3
Creating the User Interface
Creating the project
Setting up the SAPUI5 bootstrap and required libraries
Creating the view components
Creating the BSPs to deploy the application and the SAPUI5 framework.
Implementing the Controller’s methods
Create
Read
Update
Delete
Find
Model and DDIC objects
To keep our code clean and adherent to the SoC (Separation of Concerns) principle, we will use a separated class to persist the changes in our contact list. To avoid deviating too much from the main subject, the creation of the model class and dictionary objects is not presented here. You can download here the nugg file with these objects.
Implementing the Create method (POST HTTP verb)
First of all we need to declare the necessary local types and variables.
METHOD if_http_extension~handle_request.
TYPES: BEGIN OF local_type_response,
success TYPE string,
msg TYPE string,
data TYPE ztt_scnblog2,
END OF local_type_response.
* Objects
DATA: lo_contact TYPE REF TO zcl_scnblog2_contact,
lo_json_serializer TYPE REF TO zcl_json_serializer. ” Copy of the standard class CL_TREX_JSON_SERIALIZER
* Internal tables
DATA: lt_contacts TYPE STANDARD TABLE OF ztb_scnblog2.
* Structures
DATA: ls_contact TYPE ztb_scnblog2,
ls_response TYPE local_type_response.
* Variables
DATA: l_rc TYPE i,
l_json TYPE string.
* Variables
DATA: l_verb TYPE string,
l_path_info TYPE string,
l_resource TYPE string,
l_param_1 TYPE string,
l_param_2 TYPE string.
…..
Next we implement the code to handle the POST request method that corresponds to the Create Contact action.
WHEN ‘POST’. ” C (Create)
CLEAR: ls_contact,
ls_response,
l_rc.
* Retrieve form data
ls_contact-email = server->request->get_form_field(’email’).
ls_contact-firstname = server->request->get_form_field(‘firstname’).
ls_contact-lastname = server->request->get_form_field(‘lastname’).
* Create an instance of the class to persist the Contact data in the database
CREATE OBJECT lo_contact.
* Create the Contact
CALL METHOD lo_contact->create
EXPORTING
i_s_contact = ls_contact
IMPORTING
e_rc = l_rc.
IF l_rc IS INITIAL.
ls_response-success = ‘true’.
ls_response-msg = ‘User created successfully!’.“hardcoded here intentionally
ELSE.
ls_response-success = ‘false’.
ls_response-msg = lo_contact->get_message( ).
ENDIF.
* Return the form data received back to the client
APPEND ls_contact TO ls_response-data.
……
ENDCASE.
CREATE OBJECT lo_json_serializer
EXPORTING
DATA = ls_response. ” Data to be serialized
* Serialize ABAP data to JSON
CALL METHOD lo_json_serializer->serialize.
* Get JSON string
CALL METHOD lo_json_serializer->get_data
RECEIVING
rval = l_json.
* Sets the content type of the response
CALL METHOD server->response->set_header_field( name = ‘Content-Type’
value = ‘application/json; charset=iso-8859-1’ ).
* Returns the results in JSON format
CALL METHOD server->response->set_cdata( data = l_json ).
ENDMETHOD.
Let’s understand what the above code does (only the relevant parts).
- First we retrieve the form data received from the client application using the get_form_field method of the request object.
* Retrieve form data
ls_contact-email = server->request->get_form_field(’email’).
ls_contact-firstname = server->request->get_form_field(‘firstname’).
ls_contact-lastname = server->request->get_form_field(‘lastname’).
- Next we create an instance of the class zcl_scnblog2_contact that will persist the Contact data in the database and call the create method.
* Create an instance of the class to persist the Contact data in the database
CREATE OBJECT lo_contact.
* Create the Contact
CALL METHOD lo_contact->create
EXPORTING
i_s_contact = ls_contact
IMPORTING
e_rc = l_rc.
- Next we need to handle the result of the action based on the return code (l_rc). Then we set the value of the attributes success and msg accordingly.
IF l_rc IS INITIAL.
ls_response-success = ‘true’.
ls_response-msg = ‘User created successfully!’.“hardcoded here intentionally
ELSE.
ls_response-success = ‘false’.
ls_response-msg = lo_contact->get_message( ).
ENDIF.
- The information received is returned back to the client application.
* Return the form data received back to the client
APPEND ls_contact TO ls_response-data.
- Finally the data is serialized in a JSON string.
ENDCASE.
CREATE OBJECT lo_json_serializer
EXPORTING
DATA = ls_response. ” Data to be serialized
* Serialize ABAP data to JSON
CALL METHOD lo_json_serializer->serialize.
* Get JSON string
CALL METHOD lo_json_serializer->get_data
RECEIVING
rval = l_json.
- To send the JSON response to the client we need this last code.
* Sets the content type of the response
CALL METHOD server->response->set_header_field( name = ‘Content-Type’
value = ‘application/json; charset=iso-8859-1’ ).
* Returns the results in JSON format
CALL METHOD server->response->set_cdata( data = l_json ).
ENDMETHOD.
Let’s test our first method using the Postman REST client.
Select the POST method and type the URL and the form fields. Be aware of using the correct field names (email, firstname, lastname).
Click the Send button to test the service. The service should return a JSON response with the message “Contact created successfully!”.
Let’s check the database table using the SE16.
If you hit the send button again without changing the form data the service should return a message informing that the contact already exist.
The created method is finished. Let’s go to the next method, read contact.
Implementing the Read method (HTTP GET verb)
Below is the code that we need to implement to handle the read method. It’s structure is very similar to the create method.
WHEN ‘GET’. ” R (Read)
CLEAR: ls_contact,
ls_response.
CREATE OBJECT lo_contact.
* Retrieve the Contact’s email passed in the URL
ls_contact-email = l_param_1.
* Retrieve querystring data
ls_contact-firstname = server->request->get_form_field(‘firstname’).
ls_contact-lastname = server->request->get_form_field(‘lastname’).
* Read Contact’s data
CALL METHOD lo_contact->read
EXPORTING
i_s_contact = ls_contact
IMPORTING
e_t_contacts = lt_contacts.
IF NOT lt_contacts[] IS INITIAL.
ls_response-success = ‘true’.
ls_response-data[] = lt_contacts[].
ELSE.
ls_response-success = ‘false’.
ls_response-msg = lo_contact->get_message( ).
ENDIF.
Let’s test our read method.
Implementing the Update method (PUT HTTP verb)
Below is the code that we need to implement to handle the update method.
WHEN ‘PUT’. ” U (Update)
CLEAR: ls_contact,
ls_response,
l_rc.
* Retrieve the Contact’s email passed in the URL
ls_contact-email = l_param_1.
* Retrieve form data
ls_contact-firstname = server->request->get_form_field(‘firstname’).
ls_contact-lastname = server->request->get_form_field(‘lastname’).
CREATE OBJECT lo_contact.
* Update the Contact
CALL METHOD lo_contact->update
EXPORTING
i_s_contact = ls_contact
IMPORTING
e_rc = l_rc.
IF l_rc IS INITIAL.
ls_response-success = ‘true’.
ls_response-msg = ‘Contact updated successfully!’. “Hardcoded here intentionally
ELSE.
ls_response-success = ‘false’.
ls_response-msg = lo_contact->get_message( ).
ENDIF.
* Return the form data received to the client
APPEND ls_contact TO ls_response-data.
Let’s test our update method. Let’s put an “X” at the end of the contact’s first and last name.
Let’s check our table in the SE16.
Implementing the Delete method (DELETE HTTP verb)
Below is the code that we need to implement to handle the delete method.
WHEN ‘DELETE’. ” D (Delete)
CLEAR: ls_contact,
ls_response,
l_rc.
CREATE OBJECT lo_contact.
* Retrieve the Contact’s email passed in the URL
ls_contact-email = l_param_1.
* Delete the Contact
CALL METHOD lo_contact->delete
EXPORTING
i_s_contact = ls_contact
IMPORTING
e_rc = l_rc.
IF l_rc IS INITIAL.
ls_response-success = ‘true’.
ls_response-msg = ‘Contact deleted successfully!’. “Hardcoded here intentionally
ELSE.
ls_response-success = ‘false’.
ls_response-msg = lo_contact->get_message( ).
ENDIF.
Let’s test our delete method.
Now our table must be empty. Let’s check it in the SE16.
In the Part 3 of this blog series we will see the most interesting part, the creation of the user interface using SAPUI5.
Hi Christian,
Great Post. I followed the instructions and tried to do a GET to retrieve some information, which am later trying to consume from SAPUI5 application via AJAX call.
When I test the GET from Chrome Postman, I do not get the response data, just 'GET' alone.
I placed a breakpoint at IF_HTTP_EXTENSION~HANDLE_REQUEST, and ran my UI5 app and I can see that it hits the breakpoint. I see that all data is passed correctly to the response from this method. However I get a error back instead of success after this call.
Any idea how I can analyze and identify what is causing the error? Did you ever face similar issue?
Thanks,
Vidya
Hi Janu,
Thanks for your feedback.
Please send me a screenshot of the error.
Best regards,
Christian
Hi Christian,
I am trying to upload NUGG_SCNBLOG2_ALL_OBJECTS.nugg file .
its showing me following dump...plz help me.
Short text
Access via 'NULL' object reference not possible.
What happened?
Error in the ABAP Application Program
The current ABAP program "ZSAPLINK_INSTALLER" had to be terminated because it
has
come across a statement that unfortunately cannot be executed.
Error analysis
An exception occurred that is explained in detail below.
The exception, which is assigned to class 'CX_SY_REF_IS_INITIAL', was not
caught in
procedure "GETSTRUCTUREFROMATTRIBUTES" "(METHOD)", nor was it propagated by a
RAISING clause.
Since the caller of the procedure could not have anticipated that the
exception would occur, the current program is terminated.
The reason for the exception is:
You attempted to use a 'NULL' object reference (points to 'nothing')
access a component.
An object reference must point to an object (an instance of a class)
before it can be used to access components.
Either the reference was never set or it was set to 'NULL' using the
CLEAR statement.
Missing RAISING Clause in Interface
Program ZSAPLINK_INSTALLER
Include ZSAPLINK_INSTALLER
Row 833
Module type (METHOD)
Module Name GETSTRUCTUREFROMATTRIBUTES
Thanks & Regards,
Praphull
Hi Praphull,
You have to use the ZSAPLINK report, not the ZSAPLINK_INSTALLER.
Best Regards,
Christian
Hi Christian,
Thnks for helpful reply...
I have one more doubt..in part3..I have created 'zscnblog2' BSP sucessfully..imported index.html & web content from eclipse worlplace.
but for ZSAPUI5 BSP which index page i need to copy(same of zscnblog2) & which SAPUI5 files do i need to import.
as of now in the demo documnt i didnt see any project named SAPUI5(in eclipse.)
Please help me...
Regards,
Praphull
Hi,
For the ZSAPUI5 BSP you have to upload the files that are in the sapui5-static.zip.
If you unzip this file you will find the index.html in the root folder.
Best regards,
Christian
Very useful and detail blog post. Thanks much for your time Christian!
Hi Nguyen,
Thanks for your feedback!
Best regards,
Christian
Hi Christian,
Looks great so far. One problem though, in the put, get and delete calls I don't see you filling l_param_1. Therefore all of my corresponding non-push calls are failing. Did I miss something?
Thanks,
Joe
Never mind. I see where it is filled. For some reason in my system this returned a null value:
l_path_info = server->request->get_header_field( name = '~path_info' ).
To get around this I had to do the following:
l_query_string = server->request->get_header_field( name = '~query_string' ).
split l_query_string at '&' into table lt_parameters.
loop at lt_parameters into ls_parameter.
split ls_parameter at '=' into l_para_id
l_para_value.
if l_para_id = 'email'.
exit.
endif.
endloop.
Then I set ls_contact-email = l_para_value instead of l_param_1 for example. This works.
Thanks,
Joe
Hi Christian,
Very nice blog. but i am facing problem in the POSTmethod. I have used same code for the post method which you have posted above but i am not able receive data through json format. If i call post method through Postman REST client by passing json in raw tab and in json format i am not able to receive but whereas if use form-data and pass as you mentioned in the below screenshot i am able to receive the data.
sample json format: {"email":"abc@gmail.com","firstname":"abc","lastname":"xyz"}
Thanks in advance.
Hi Siddeshwara,
Thanks for you feedback.
The REST service implemented for this blog works only with form-data submission. In order to work with raw data submission you will have to retrieve the json data sent as raw data by calling the method GET_CDATA of the request object. After that, you will have parse the json data so you can use it.
Regards,
Christian
Thanks a lot...Its working now. one more help.
I am able to deserialize one json array.Eg: it_data[{"email":"abc@gmail.com"}] but if i am having two array like {it_data[{"email":"abc@gmail.com"}],it_output[{"name":"abc"}]} i am not able to parse it. I will be able to parse only dividing json string into two array and then parse it.
Is there any other way to parse it if i am having multiple array in json string.
Hi Siddesh,
I suggest you to use zJSON to parse it.
se38/zJSON · GitHub
(usage examples)
Usage zJSON · se38/zJSON Wiki · GitHub
Regards,
Christian
Thank you very much 🙂
Hi Christian,
I am not able to call my SICF service in SAP UI5 ajax call. please find below code.
//ajax call
$.ajax({
url: 'http://.../sap/bc/test_sui5',
type: 'GET',
contentType : "application/json",
dataType: 'json',
username:'abc',
password:'abc123',
timeout: 10000,
cache: false,
success: function(data, textStatus, xhr) {
alert("success");
alert(textStatus);
},
error: function(xhr, textStatus, errorThrown) {
alert("error");
alert(textStatus);
}
});
please help me to resolve this issue.
Note: I am calling ICF Service directly in SAP UI5 Ajax call.
Regards,
Sid
For all having Malformed JSON as the result.
Don't use the standard CL_TREX_JSON_SERIALIZER. Copy it to ZCL_TREX_JSON_SERIALIZER and change according to the following blog post:Serialize ABAP data into JSON format
Yes, as described in the part 1 of this blog post in the Prerequisites section (last item).
Thanks,
Christian Jianelli
Yeah, I guess my note is for someone like me, who thought that in 2015 in NW 7.4 SAP has patched the standard CL_TREX_JSON_SERIALIZER to serialize JSON correctly 🙂