proper way to handle dynamic responses by retrofit 2 - java

let's say I've got a REST API which I could get list of books by invoking following retrofit 2 request.
public interface AllRecordsFromRequestInterface {
#GET("books/all")
Call<List<TrackInfo>> operation(#Header("Authorization") String authentication_token);
}
and API response:
[
{
"id": "1",
"title": "The Catcher in the Rye",
"author":"J. D. Salinger"
},
{
"id": "2",
"title": "The Great Gatsby",
"author":"F. Scott Fitzgerald"
}
]
I use GsonConverterFactory to convert json to a Model. here is my model class
public class Book{
private int id;
private String title;
private String author;
}
I'm using a authentication token to authorize myself to API as it can be seen in my request. some times other response are received rather than above response because of token expiration or something else. for example:
{
"status": "error",
"message": "Expired token"
}
what is the proper way to handle dynamic responses (with known structure) in retrofit 2?

you have multiple choices:
1-change your API:(this one is standard)
change it like this for every response and if the user failed with authentication leave the result null or if authentication was successful put the list in the result.
{
"status" : "error/success"
"message" : ...
"result" : ....
}
2- you can give Object type to retrofit and after the response was successful you can cast it to one of your models, using "instance of" syntax.
public interface AllRecordsFromRequestInterface {
#GET("books/all")
Call<Object> operation(#Header("Authorization") String authentication_token);
}

Related

Spring list validation - get field from invalid object

I'm trying to understand if it's possible to get the index of invalids objects inside a list that is validated with #Valid.
I already have validation in place where I send a response like this
{
"status": "BAD_REQUEST",
"message": "Validation Error",
"detailedMessage": "There are errors in the entered data",
"timestamp": 1657896844868,
"validationErrors": [
{
"field": "items[0].name",
"message": "The name is mandatory"
},
{
"field": "items[1].surname",
"message": "The surname is mandatory"
}
]
}
The problem is that in the frontend I need to know exactly which objects in the array "items" have problems so I can highlight the corret input. What I'm doing right now is getting the index from the string "items[0].name" using a regex but I really dislike this kind of behavior and I would like to exctract the index of the invalid item and put it in the response.
Ideally I would like to not have the array index but a specific field of the invalid object.
What I mean is that every item has an "id" field and I would like to extract that one and send something like this in response
{
"status": "BAD_REQUEST",
"message": "Validation Error",
"detailedMessage": "There are errors in the entered data",
"timestamp": 1657896844868,
"validationErrors": [
{
"field": "items[12345].name",
"message": "The name is mandatory",
"itemId": 12345
},
{
"field": "items[12346].surname",
"message": "The surname is mandatory",
"itemId": 12346
}
]
}
In this way I would be able to know exactly which object is invalid in the frontend without having to rely on array indexes or regex to extract the index from a string. Of course having the array index as "itemIndex" field would also be better than what I have right now.
Following you can find my request class, that is used in the controller as #Valid #RequestBody, and my ControllerAdvice where I build my response when a MethodArgumentNotValidException happens.
Request
public class Request {
#Valid
#NotEmpty(message = "You must insert at least one item")
private List<#Valid #NotNull Item> items;
}
ControllerAdvice
#RestControllerAdvice
public class BaseExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.BAD_REQUEST, "Validation Error", "There are errors in the entered data");
List<ValidationError> errors = new ArrayList<>();
ex.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
ValidationError validationError = new ValidationError(fieldName, errorMessage);
errors.add(validationError);
});
errorResponse.setValidationErrors(errors);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}
You can access information about objects that violated the constraint by unwrapping your ObjectError or FieldError to ConstraintViolation (https://docs.oracle.com/javaee/7/api/javax/validation/ConstraintViolation.html).
Here is how you can access array index
Path nodes = error.unwrap(ConstraintViolation.class).getPropertyPath();
for (Path.Node node : nodes) {
if (node.isInIterable()) {
node.getIndex() // for items[12346].surname it will return 123456
}
}
Accessing an element that has thrown an error is also possible using unwrap to ConstraintViolation.
Item item = (Item) error.unwrap(ConstraintViolation.class).getLeafBean(); // don't use cast with no checks in your code
This will return an item which triggered constraint violation.

Adding additional field in Response Object

I am getting below response when I am calling an API.
Response postRequestResponse = ConnectionUtil.getwebTarget()
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.path("bots")
.path(ReadSkillID.readSkillId())
.path("dynamicEntities").path(dynamicEntityID)
.path("pushRequests").path(pushRequestID).path(operation)
.request()
.header("Authorization", "Bearer " + ConnectionUtil.getToken())
.get();
Below output I am getting.
{
"createdOn": "2020-08-17T12:19:13.541Z",
"updatedOn": "2020-08-17T12:19:23.421Z",
"id": "C84B058A-C8F9-41F5-A353-EC2CFE7A1BD9",
"status": "TRAINING",
"statusMessage": "Request Pushed into training, on user request"
}
I have to return this output to client with an additional field in the response. How can modify the above response and make it
{
"EntityName": "NewEntity", //New field
"createdOn": "2020-08-17T12:19:13.541Z",
"updatedOn": "2020-08-17T12:19:23.421Z",
"id": "C84B058A-C8F9-41F5-A353-EC2CFE7A1BD9",
"status": "TRAINING",
"statusMessage": "Request Pushed into training, on user request"
}
I am adding this additional field here
"EntityName": "NewEntity"
How can I do that. many things I tried but got exception.
get JSON from postRequestResponse (i have no idea what framework you are using, so you have to figer it out on your own, but the Response datatype will probably have a getResponseBody or similar method returing the JSON)
add EntityName
serialize it again to json.
class YourBean {
#Autowired
private ObjectMapper objectMapper;
public void yourMethod() {
// 1
final InputStream jsonFromResponse = ...
// 2
Map dataFromResponse = objectMapper.readValue(jsonFromResponse, Map.class);
dataFromResponse.put("EntityName", "NewEntity");
// 3
final String enrichedJson = objectMapper.writeValueAsString(dataFromResponse);
}
}
enrichedJson contains EntityName and whatever comes from the API.

How to get Spring rest API response in java code?

I done Spring rest api .that is returning json data now i want to call that api in my system(remote by ip) .how to get that response in my java code
Or Rest Template.
{[
{
"deviceId": 1,
"userId": "100",
"userName": "Jee",
"date": "2016-09-19 00:00:00.000"
},
.
.
.
n
]}
how to read in java using rest Template .
What you want is consuming a rest api in Java. This should get you started.
Its a pretty much a basic need and its all over the web on how to consume rest apis.
This and this are good starting points.
Quoting the relevant parts here:
Use POST to Create a Resource
In order to create a new Resource in the API – we can make good use of the postForLocation(), postForObject() or postForEntity() APIs.
The first returns the URI of the newly created Resource while the second returns the Resource itself.
4.1. The postForObject API
ClientHttpRequestFactory requestFactory = getClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);
assertThat(foo, notNullValue());
assertThat(foo.getName(), is("bar"));
4.2. The postForLocation API
Similarly, let’s have a look at the operation that – instead of returning the full Resource, just returns the Location of that newly created Resource:
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
URI location = restTemplate.postForLocation(fooResourceUrl, request);
assertThat(location, notNullValue());
4.3. The exchange API
Finally, let’s have a look at how to do a POST with the more generic exchange API:
RestTemplate restTemplate = new RestTemplate();
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
ResponseEntity<Foo> response = restTemplate.
exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
Foo foo = response.getBody();
assertThat(foo, notNullValue());
assertThat(foo.getName(), is("bar"));
You'd do good with the post exchange API.
Update
Your json response is not valid. You need to change the rest api to return something like this
{
"results": [
{
"deviceId": 1,
"userId": "100",
"userName": "Jee",
"date": "2016-09-19 00:00:00.000"
},
{
"deviceId": 1,
"userId": "100",
"userName": "Jee",
"date": "2016-09-19 00:00:00.000"
}
]
}
When consuming this rest api, the response object will be
public class ResponseObject {
private List<BiomatrixResult> results;
//getter setters
}
where your BiomatrixResult object will be
public class BiomatrixResult {
private int deviceId;
private String userId;
private String userName;
private Date date;
//getters setters
}

Spring 4 RestController: not receiving data from PUT request

I'm trying to build a very basic REST API using Spring.
My URL endpoints are:
GET /notes
GET /notes/{noteId}
POST /notes
PUT /notes/{noteId}
DELETE /notes/{noteId}
All these endpoints work perfectly fine as expected except the PUT request which I want to run to update an item.
The problem is that data is not being received via PUT, where as it works fine for POST.
Here's my controller; I've tested it by adding a method identical to the update method but using POST and that works fine. I don't know why?
package notes;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/notes")
public class NotesController {
...
#RequestMapping(value="/{noteId}", method=RequestMethod.PUT)
public Response update(#PathVariable Integer noteId, Note note) {
return new Response("Note Updated", note);
}
#RequestMapping(value="/{noteId}", method=RequestMethod.POST)
public Response updateWithPost(#PathVariable Integer noteId, Note note) {
return new Response("Note Updated", note);
}
...
}
Using postman, I've tested POST http://127.0.0.1:8080/notes/5 and the response was:
{
"message": "Note Updated",
"note": {
"id": null,
"title": "Hello World detail content",
"text": "Hello World",
"createdAt": "72, Mar 2015",
"updatedAt": "72, Mar 2015"
}
}
But for PUT http://127.0.0.1:8080/notes/5 with exactly same data the response was:
{
"message": "Note Updated",
"note": {
"id": null,
"title": "",
"text": "",
"createdAt": "72, Mar 2015",
"updatedAt": "72, Mar 2015"
}
}
Update Request
For both PUT & POST request I'm sending the same test data:
title: Hello World
text: Hello World detail content
Response Class
package notes;
public class Response {
private String message;
private Note note;
public Response(String text) {
setMessage(text);
}
public Response(String text, Note note) {
setMessage(text);
setNote(note);
}
//...getters/setters
}
Note Class
package notes;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Note {
private Integer id = null;
private String title = "";
private String text = "";
private Date createdAt = new Date();
private Date updatedAt = new Date();
public Note() {}
public Note(String title, String text) {
this.setTitle(title);
this.setText(text);
}
//...getters/setters
}
ApplicationConfig
package notes;
import org.springframework.context.annotation.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#EnableWebMvc
#Configuration
public class ApplicationConfig {
}
I don't know why this is not working?
This is a limitation of the Servlet Spec and the inner workings of Spring for populating model attributes.
First, the spec says
3.1.1 When Parameters Are Available
The following are the conditions that must be met before post form data will be populated to the
parameter set:
The request is an HTTP or HTTPS request.
The HTTP method is POST.
The content type is application/x-www-form-urlencoded.
The servlet has made an initial call of any of the getParameter family of methods on the request object. If the conditions are not met
and the post form data is not included in the parameter set, the post
data must still be available to the servlet via the request object's
input stream. If the conditions are met, post form data will no longer
be available for reading directly from the request object's input
stream.
Second, your handler methods' second parameter, the one of type Note, is actually considered a model attribute, as if it was implicitly annotated with #ModelAttribute. As such, it is processed by Spring's ModelAttributeMethodProcessor. This HandlerMethodArgumentResolver uses the getParameter (and its family of methods) for populating the created instance's fields.
Since this is a PUT request, the parameters are not retrievable through getParameter. However, they are still accessible through the request body, but Spring doesn't go there for model attributes.
You can do the conversion yourself. But I suggest you change your request content to JSON or XML for PUT and use #RequestBody annotated parameters.
There is a long standing request to add parameters for PUT requests.
Don't know if it's the case here (or if it is even relevant nowadays, haven't worked with REST-stuff for a while), but I've hit a problem in the past where Jetty and Tomcat handled PUT-requests differently (the application itself was using Spring MVC similarly as your code), the other expected the parameters to be in the URL and the other wanted them in the body, like with POST.
Apparently the HTTP-spec isn't very precise on this, so different containers can handle it differently. More information here.

How do I deserialize Drupal JSON Services strings in Android?

I am using Drupal Services along with the JSON Services module as a data source.
I am using the DrupalCloud library, https://github.com/skyred/DrupalCloud/wiki, and am wondering how to best process the results that I receive from a userLogin() call.
If the call itself fails we get:
{
"#error": true,
"#message": "Some message"
}
If the call succeeds but the login credentials are wrong:
{
"#error": false,
"#data": {
"#error": true,
"#message": "Some message"
}
}
If the call success and the login credentials are correct, it returns:
{
"#error": false,
"#data": {
"sessid": "foo",
"user": {
"uid": "69",
"name": "Russell Jones",
"pass": "bar",
"mail": "russell#test.net",
"roles": {
"2": "authenticated user",
"5": "Student"
},
}
}
}
How do I go about using this data meaningfully? Or rather, how do I test to see if the call worked, and if the login was successful or not.
Have you searched older posts? Like this post, from 2 hours ago:
how to convert json object into class object
or: JSON Parsing in Android
Or just search for yourself: Search: Android+Json
Should give you a good idea..
This is one way to parse the last JSON message in your question:
public void readJsonString(String jsonString) throws JSONException
{
JSONObject jsonObject = new JSONObject(jsonString);
boolean error = jsonObject.getBoolean("#error");
if (!error)
{
JSONObject data = jsonObject.getJSONObject("#data");
String sessionId = data.getString("sessid");
JSONObject user = data.getJSONObject("user");
int uid = user.getInt("uid");
String name = user.getString("name");
// you get the pattern, same way with the other fields...
}
}

Categories

Resources