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.
Related
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.
I'm implementing query layer on database by using GraphQl and spring boot project to perform CRUD operation on sql database. In GraphQL schema i mentioned some fields to be mandatory and when those fields are not mentioned in query it is returning ValidationError error message in default format with 200 status code.
Error :
{
"data": null,
"errors": [
{
value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]' # 'create_book'",
"locations": [
{
"line": 3,
"column": 23,
"sourceName": null
}
],
"description": "argument 'insert' with value value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]'",
"validationErrorType": "WrongType",
"queryPath": [
"create_book"
],
"errorType": "ValidationError",
"path": null,
"extensions": null
}
],
"dataPresent": false,
"extensions": null
}
And here is my code with layer architecture pattern
Controller :
#Autowired
private GraphQLServer graphQlServer;
#PostMapping("test")
public ResponseEntity<Object> graphQl(#RequestBody String body){
ExecutionResult response = graphQlServer.execute(body);
return ResponseEntity.ok(response);
}
Service :
#Service
public class GraphQLServer {
#Autowired
private GraphQL graphQl;
public ExecutionResult execute(String query) {
return graphQl.execute(query);
}
}
Config :
#Bean
public GraphQL loadSchema() throws IOException {
File schemaFile = schemaResource.getFile();
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
RuntimeWiring wiring = buildRuntimeWiring();
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
return GraphQL.newGraphQL(schema).build();
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Mutation", mutationWiring -> mutationWiring.dataFetcher("create_book", bookDataFetcher))
.build();
}
BookDataFetcher :
#Override
public Map<String, Object> get(DataFetchingEnvironment environment) {
//return data from db by getting Map properties from environment
}
The above code is working as expected but my question here is How to customize the error message? In the error message i would like to mention the status 400 since it is bad request from client
First of all , you should call toSpecification() on ExecutionResult to make sure the response obeys the GraphQL Specification.
By default , there is only one ExecutionResult 's implementation provided by graphql-java which is ExecutionResultImpl , so you can cast ExecutionResult to it in order to use its transform() to update its state.
ExecutionResultImpl internally contains all errors detected by the graphql-java. All of them are in the subclass of GraphQLError which mean you have to cast it to the specific sub-class during customization.
In your case , the subclass is ValidationError and the codes look something like :
#PostMapping("test")
public ResponseEntity<Object> graphQl(#RequestBody String body){
ExecutionResult response = graphQlServer.execute(body);
ExecutionResultImpl responseImpl = (ExecutionResultImpl) response;
List<GraphQLError> customizedErrors = Lists.newArrayList();
for (GraphQLError gqlError : responseImpl.getErrors()) {
//Do your error custmosation here....
GraphQLError customizedError = gqlError;
if (gqlError instanceof ValidationError) {
ValidationError error = (ValidationError) gqlError;
customizedError = new ValidationError(error.getValidationErrorType(), error.getLocations(),
"Customizing some error message blablabla....");
}
customizedErrors.add(customizedError);
}
Map<String, Object> specResponse = responseImpl.transform(b->b.errors(customizedErrors)).toSpecification();
return ResponseEntity.ok(specResponse);
}
I am trying to consume api with two different responses. when providing proper input and headers, it gives one json object. That is working fine with code given below.
but when one of the inputs or headers are missing then api is returning other json which states error details while hitting through Postman but same is not getting achieved by client code. It throws exception but api should return json with error details.
first I tried with postForobject(), then changed to exchange() assuming postForObject returning object and api is not getting expected object format. so tried with String.clas instead of particular class
what can be done to get two different json object by hitting same url?
when success:
{
"contacts": [
{
"input": "98########",
"status": "valid"
}
]
}
when input or header missing:
"errors": [
{
"code": 1###,
"title": "Access denied",
"details": "invalid deatils"
}
]
below is my code:
public static void main(String[] args) throws Exception {
RestTemplate restTemplate = new RestTemplate();
String check_Contact_Async = "https://port/contacts/";
sslByPass();
//headers = setHeaders();
contactsDTO = getInput();
final HttpEntity<ContactsDTO> entity = new HttpEntity<ContactsDTO>(contactsDTO, headers);
try {
//WsResponse res = restTemplate.postForObject(check_Contact_Async, entity, WsResponse.class);
ResponseEntity<String> responseEntity = restTemplate.exchange(
check_Contact_Async, HttpMethod.POST, entity,
String.class);
System.out.println(responseEntity);
} catch (Exception exception) {
System.out.println(exception);
}
}
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:498)
at com.websystique.springmvc.apitest.Example.main(Example.java:120)
any clue will be helpful
Thanks
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);
}
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
}