In the Open Event Organizer Android app the challenge is to provide user, a readable error description for the input requests. The Organizer App was showing a coded message which was understandable only to a programmer making it unfriendly to the common user. The blog describes how we tackled this problem and implemented a mechanism to provide user friendly error messages for user requests (if any).
Let’s consider the scenario when an organizer want to create an Event or maybe a Ticket so what he has to do is fill out a form containing some user input fields and click on submit button to send the data to the server which in turn sends a response for the request. If the information is valid the server sends a HTTP 201 Response and user gets feedback that the Event/Ticket is created. But if the information is not valid the user gets feedback that there is HTTP 422 error in your request. There is no readable description of error provided to the user.
To handle this problem we will be using Retrofit 2.3.0 to make Network Requests, which is a REST client for Android and Java by Square Inc. It makes it relatively easy to retrieve and upload JSON (or other structured data) via a REST based Web Service. Further, we will be using other awesome libraries like RxJava 2.1.10 (by ReactiveX) to handle tasks asynchronously, Jackson, Jasminb-Json-Api in an MVP architecture. Let’s move on to the code.
Retrofit to the rescue
Now we will see how we can extract the details about the error response and provide user a better feedback rather than just throwing the error code.
Firstly let’s make the Request to our REST API Server using Retrofit2.
@POST(“faqs”) Observable<Faq> postFaq(@Body Faq faq); |
Here we are making a POST request and expecting a Observable<Faq> Response from the server.The @Body annotation indicates the Request Body is an Faq object.
Please note that Observable used here is ReactiveX Observable so don’t confuse it with java.util Observable.
public class Faq { @Id(LongIdHandler.class) public Long id; @Relationship(“event”) @ForeignKey(stubbedRelationship = true, onDelete = ForeignKeyAction.CASCADE) public String question; |
Let’s say the API declares both question and answer as mandatory fields for creation of an Faq. We supply the following input to the app.
question = “Do I need to buy a Ticket to get In ?”; answer = null |
We used RxJava to make an asynchronous request to server. In case of error we will get a Retrofit throwable and we will pass that on to ErrorUtils.java which will do all the processing and return a readable error message.
faqRepository .createFaq(faq) .compose(dispose(getDisposable())) .compose(progressive(getView())) .doOnError(throwable -> getView().showError(ErrorUtils.getMessage(throwable))) |
Now we will extract the ResponseBody from the Retrofit throwable.
ResponseBody responseBody = ((HttpException) throwable)
.response().errorBody(); |
The ResponseBody is a JSON containing all the information about the error.
{ “errors”: [ { “status”: “422”, “source”: { “pointer”: “/data/attributes/answer” }, “detail”: “Missing data for required field.”, “title”: “Validation error” } ], “jsonapi”: { “version”: “1.0” } } |
In order to provide better feedback to user regarding the error we have to parse the response JSON and extract the pointed field (which in this case is – “answer”) and then combine it with the value corresponding to the “detail” key. Following is the relevant section from ErrorUtils.java (for viewing the complete ErrorUtils.java please refer here).
public static String getErrorDetails(ResponseBody responseBody) { try { JSONObject jsonObject = new JSONObject(responseBody.string()); JSONObject jsonArray = new JSONObject(jsonObject.getJSONArray(“errors”).get(0).toString()); JSONObject errorSource = new JSONObject(jsonArray.get(“source”).toString()); try { getPointedField(errorSource.getString(“pointer”)); .replace(“.”, “”) + “: ” + pointedField; } catch (Exception e) { public static String getPointedField(String pointerString) { |
The final error message returned by ErrorUtils in case of HTTP 422 –
Missing data for required field: answer |
Similar approach can be followed for extracting the error “title” or “status” .
References
- Official documentation of Retrofit 2.x http://square.github.io/retrofit/
- Official documentation for RxJava 2.x https://github.com/ReactiveX/RxJava
- Codebase for Open Event Organizer App on Github https://github.com/fossasia/open-event-orga-app