Invalid JSON content generated by Spring Controller (using Jackson library) - java

For some reason unknown to me, when making a request to my Spring controller it is returning an invalid JSON value. I'm using Jackson to map my JSON object. This is the data being returned when I make the request:
{} &&
{
"registros": [
{
"id": 251,
"matricula": "32849923",
"dadoPessoal": {
"nome": "Testando",
"email": "tiare#terra.com.br",
"telefone": "1235324812",
"celular": "123832911",
"foto": null,
"salario": 3829
},
"status": true,
"nascimento": {
"dataNascimento": 1417485600000,
"nascionalidade": "Brasil",
"localNascimento": "SP"
},
"documentoPessoal": {
"rg": "8329892332",
"orgaoEmissor": "SSP/SP",
"dataEmissao": 1417485600000,
"cpf": "016.015.XXX-XX",
"tituloEleitor": "adw91021",
"zonaDeVoto": "91aa",
"sessaoVoto": "2a",
"enderecoVoto": "adw"
},
"dataAdmissao": 1361674800000,
"dataDesligamento": null,
"version": 0
}
],
"total": 1
}
The problem here is that somehow invalid characters "{} &&" are being added to the beginning of the JSON. What I'm not understanding is how? Its adding these values that are not defined anywhere in my Spring method.
My Request Mapping:
#Override
#RequestMapping(value = { "/", "" }, method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView view = new ModelAndView(VIEW_INDEX);
view.addObject("registros", service.findAll());
view.addObject("total", service.findAll().size());
return view;
}
The Jackson configuration:
/**
* #return MappingJacksonJsonView
*/
#Bean
public MappingJacksonJsonView mappingJacksonJsonView() {
MappingJacksonJsonView mappingJacksonJsonView = new MappingJacksonJsonView();
mappingJacksonJsonView.setContentType("application/json");
mappingJacksonJsonView.setObjectMapper(this.objectMapper());
mappingJacksonJsonView.setEncoding(JsonEncoding.UTF8);
mappingJacksonJsonView.setPrefixJson(true);
return mappingJacksonJsonView;
}
/**
* #return ContentNegotiatingViewResolver
*/
#Bean
public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {
List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();
// Tiles
viewResolvers.add(this.tileViewResolver());
// Views
List<View> defaultViews = new ArrayList<View>();
defaultViews.add(this.mappingJacksonJsonView());
ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
contentNegotiatingViewResolver.setViewResolvers(viewResolvers);
contentNegotiatingViewResolver.setDefaultViews(defaultViews);
contentNegotiatingViewResolver.setOrder(0);
return contentNegotiatingViewResolver;
}

Thanks you all. I found the error!!
When i started to read the javadoc i found this:
"Indicates whether the JSON output by this view should be prefixed with "{} && ". Default is false."
springsource docs

Related

Spring Boot (2.3.6.RELEASE) Deserialization Fails when using RestTemplate and Unwrap Root

I'm trying to consume an API using RestTemplate but it will simply not deserialize the json response into my pojo
Here is the json payload I'm trying to deserialize:
"Response": {
"Count": 77,
"Data": [
{
"AllowDelete": "1",
"ContactCount": 1482,
"CreatedDate": "Dec 01, 2020",
"ID": "17991951",
"IsImporting": "0",
"IsMasterUnsubscribe": "0",
"ListAudited": "1",
"ListDescription": "City of Markham Staff - December 2020 (LATEST)",
"ListImportV3": "1",
"ListType": "0",
"ModifiedDate": "Dec 03, 2020",
"Name": "City of Markham Staff - December 2020 (LATEST)",
"NameShort": "City of Markham Staff - December 2020 (LATEST)",
"PermissionPassList": "0",
"Segments": [],
"Status": ""
},{
"AllowDelete": "0",
"ContactCount": 884,
"CreatedDate": "Nov 04, 2011",
"ID": "582203",
"IsImporting": "0",
"IsMasterUnsubscribe": "1",
"ListAudited": "1",
"ListDescription": "Master Unsubscribe List",
"ListImportV3": "0",
"ListType": "0",
"ModifiedDate": "Dec 04, 2020",
"Name": "Master Unsubscribe List",
"NameShort": "Master Unsubscribe List",
"PermissionPassList": "0",
"Segments": [],
"Status": ""
}
],
"Status": "1"
}
}
Here is my main pojo:
package com.markham.enews.model;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonRootName(value = "Response")
public class Contact {
//Total number
private int count;
//1 if successful, -1 if error
private String status;
// Further details of the Contact List
private List<ContactFullRecord> data;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<ContactFullRecord> getData() {
return data;
}
public void setData(List<ContactFullRecord> data) {
this.data = data;
}
#Override
public String toString() {
return "Contact [count=" + count + ", status=" + status + ", data=" + data + "]";
}
}
As per this stack overflow link Spring Boot Jackson with Root name
I added the following to my application.properties:
spring.jackson.mapper.accept-case-insensitive-properties=true
spring.jackson.deserialization.unwrap-root-value=true
My rest controller get method is as follows:
#GetMapping(value = "/ContactTest")
private Contact getContactTest() {
String uri = "https://clientapi.benchmarkemail.com/Contact/";
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> request = new HttpEntity<String>(createHeaders());
ResponseEntity<Contact> response = restTemplate.exchange(uri, HttpMethod.GET, request, Contact.class);
Contact contact = response.getBody();
return contact;
}
But the resulting object has all empty/null values:
"count": 0,
"status": null,
"data": null
I think the unwrap root and/or case insensitive properties are not being picked up..
If I write the following unit test and use objectMapper directly, it works:
#Test
public void wrapRootValue() throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
String str = "{ \"Response\": {\"Count\": 77,\"Data\": [{\"AllowDelete\": \"0\",\"ContactCount\": 884,\"CreatedDate\": \"Nov 04, 2011\",\"ID\": \"582203\",\"IsImporting\": \"0\",\"IsMasterUnsubscribe\": \"1\",\"ListAudited\": \"1\",\"ListDescription\": \"Master Unsubscribe List\",\"ListImportV3\": \"0\",\"ListType\": \"0\",\"ModifiedDate\": \"Dec 03, 2020\",\"Name\": \"Master Unsubscribe List\",\"NameShort\": \"Master Unsubscribe List\",\"PermissionPassList\": \"0\",\"Segments\": [],\"Status\": \"\"}],\"Status\": \"1\"}}";
Contact root = mapper.readValue(str, Contact.class);
System.out.println(root);
}
Output:
Contact [count=77, status=1, data=[ContactFullRecord [id=582203, name=Master Unsubscribe List, nameShort=Master Unsubscribe List, status=, contactCount=884.0, createdDate=Nov 04, 2011, modifiedDate=Dec 03, 2020, permissionPassList=0, listAudited=1, listDescription=Master Unsubscribe List, isImporting=0, isMasterUnsubscribe=1, allowDelete=0, listImportV3=0]]]
Any help would be greatly appreciated!
Use spring boot pre configured RestTemplateBuilder ( has all the jackson message converter configuration applied ) and use build to request new RestTemplate instance.
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
}
Autowire the instance into controller class.
#Autowired
private RestTemplate restTemplate;
#GetMapping(value = "/ContactTest")
private Contact getContactTest() {
String uri = "https://clientapi.benchmarkemail.com/Contact/";
HttpEntity<String> request = new HttpEntity<String>(createHeaders());
ResponseEntity<Contact> response = restTemplate.exchange(uri, HttpMethod.GET, request, Contact.class);
Contact contact = response.getBody();
return contact;
}
You can also look at https://www.baeldung.com/spring-rest-template-builder for other set ups.
The problem is that you are configuring the Jackson deserialization behavior at the Spring Boot level, you are not configuring the deserialization behavior for your RestTemplate.
One possible approach you can follow is the one suggested by #s7vr in his/her answer, and reuse the Spring Boot provided configuration.
If you only want to customize the Jackson configuration for your RestTemplate you can do it with something like:
final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
// Base converters
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter(false));
messageConverters.add(new SourceHttpMessageConverter<>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
// Custom Jackson Converter
final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
final ObjectMapper mapper = mappingJackson2HttpMessageConverter.getObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
messageConverters.add(mappingJackson2HttpMessageConverter);
final RestTemplate restTemplate = new RestTemplate(messageConverters);
// Use it as you consider appropriate
String uri = "https://clientapi.benchmarkemail.com/Contact/";
HttpEntity<String> request = new HttpEntity<String>(createHeaders());
ResponseEntity<Contact> response = restTemplate.exchange(uri, HttpMethod.GET, request, Contact.class);
Contact contact = response.getBody();
//...
Of course, you can reuse this configuration if needed by configuring a FactoryBean for RestTemplate and inject later in your controller, for instance.

How to customize GraphQL query validation error message

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);
}

Spring HATEOAS build link to paged resource

I have a controller with method, which returns PagedResource, which looks like this:
#RequestMapping(value = "search/within", method = RequestMethod.POST)
public #ResponseBody PagedResources within(#RequestBody GeoJsonBody body,
Pageable pageable, PersistentEntityResourceAssembler asm) {
// GET PAGE
return pagedResourcesAssembler.toResource(page, asm);
}
Now, I want to add that method as a link to the root resource, so I do the following:
public RepositoryLinksResource process(RepositoryLinksResource repositoryLinksResource) {
repositoryLinksResource.add(linkTo(methodOn(ShipController.class).within(null, null, null)).withRel("within"));
return repositoryLinksResource;
}
Which works and I get my link, however it add that link without pagination parameters. So it look like this:
"within": {
"href": "http://127.0.0.1:5000/search/within"
},
and I want to turn it into:
"within": {
"href": "http://127.0.0.1:5000/search/within{?page, size}"
},
This previous question on stackoverflow suggests that after fixing the corresponding issue on GitHub it should work by default, however, it doesn't.
What am I doing wrong ?
Automatic Creation of Paginated Links with PagedResourcesAssembler
I had success using PagedResourcesAssembler. Let's say your entity is called
MyEntity. Your within method should return HttpEntity<PagedResources<MyEntity>>.
Your within method should look something similar to the example shown below.
#RequestMapping(value = "search/within", method = RequestMethod.POST)
#ResponseBody
public HttpEntity<PagedResources<MyEntity>>
within(#RequestBody GeoJsonBody body,Pageable pageable,
PagedResourcesAssembler assembler) {
// GET PAGE
Page<MyEntity> page = callToSomeMethod(pageable);
return new ResponseEntity<>(assembler.toResource(page), HttpStatus.OK);
}
Here is a simple example. In this example, the response looked like the one shown below,
{
"_embedded": {
"bookList": [
{
"id": "df3372ef-a0a2-4569-982a-78c708d1f609",
"title": "Tales of Terror",
"author": "Edgar Allan Poe"
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/books?page=0&size=20"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Manual Creation of Paginated Self Link
If you're interested in creating the paginated link manually, here's the code snippet you can use,
Page<MyEntity> page = callToSomeMethod(pageable);
ControllerLinkBuilder ctrlBldr =
linkTo(methodOn(ShipController.class).within(body, pageable, asm));
UriComponentsBuilder builder = ctrlBldr.toUriComponentsBuilder();
int pageNumber = page.getPageable().getPageNumber();
int pageSize = page.getPageable().getPageSize();
int maxPageSize = 2000;
builder.replaceQueryParam("page", pageNumber);
builder.replaceQueryParam("size", pageSize <= maxPageSize ?
page.getPageable().getPageSize() : maxPageSize);
Link selfLink =
new Link(new UriTemplate(builder.build().toString()), "self");

Bean Validation + JAX-RS not reading custom ValidationMessages.properties

I am trying to use custom messages with Bean Validation but my custom messages are not being returned by the JAX-RS resource. What am I doing wrong?
ValidationMessages.properties
invoice.value.notnull=Invoice value must be informed.
The file is located at src/main/resources
InvoiceResource.java
#Path("/invoice")
public class InvoiceResource {
#POST
public void post(#Valid InvoiceRequest request) {
/* stuff */
}
}
InvoiceRequest.java
public class InvoiceRequest {
#NotNull(message = "invoice.value.notnull")
private Double value;
}
Found out that the problem was my declaration of the message in the bean param. The message ID must be forked between braces "{ ... }":
#NotNull(message = "{invoice.value.notnull}")
How we get the proper JSON response with the correct message:
{
"exception": null,
"fieldViolations": [],
"propertyViolations": [],
"classViolations": [],
"parameterViolations": [
{
"constraintType": "PARAMETER",
"path": "post.arg0.value",
"message": "Invoice value must be informed",
"value": ""
}
],
"returnValueViolations": []
}

How to Receive Webhook from Stripe in Java

I am trying to receive a webhook via a post request from Stripe Payments. The java method to process it looks like this:
#ResponseBody
#RequestMapping( consumes="application/json",
produces="application/json",
method=RequestMethod.POST,
value="stripeWebhookEndpoint")
public String stripeWebhookEndpoint(Event event){
logger.info("\n\n" + event.toString());
logger.info("\n\n" + event.getId());
return null;
}
But the Stripe Event always comes back with all null values:
<com.stripe.model.Event#315899720 id=null> JSON: {
"id": null,
"type": null,
"user_id": null,
"livemode": null,
"created": null,
"data": null,
"pending_webhooks": null
}
If the method receives a String instead,and using #RequestBody:
#ResponseBody
#RequestMapping( consumes="application/json",
produces="application/json",
method=RequestMethod.POST,
value="stripeWebhookEndpoint")
public String stripeWebhookEndpoint(#RequestBody String json){
logger.info(json);
return null;
}
Here, it prints the json without null values. Here's part of the request being printed:
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "charge.succeeded",
"object": "event",
"request": null,
"data": {
"object": {
"id": "ch_00000000000000",
"object": "charge",
"created": 1389985862,
"livemode": false,
"paid": true,
"amount": 2995,
"currency": "usd",
...
}
But using #RequestBody with a Stripe Event parameter gives a 400: bad syntax.
So why can't I take in the correct type, a Stripe Event, as the parameter?
Here's what I did:
The Java method still takes in the Event as a json String. Then I used Stripe's custom gson adapter and got the Event with:
Event event = Event.gson.fromJson(stripeJsonEvent, Event.class);
Where stripeJsonEvent is the string of json taken in by the webhook endpoint.
public String stripeWebhookEndpoint(#RequestBody String json, HttpServletRequest request) {
String header = request.getHeader("Stripe-Signature");
String endpointSecret = "your stripe webhook secret";
try {
event = Webhook.constructEvent(json, header, endpointSecret);
System.err.println(event);
} catch (SignatureVerificationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//
enter code here
return "";
}
I have been looking for the same answer, so after looking at their own code, here is how they actually do it:
String rawJson = IOUtils.toString(request.getInputStream());
Event event = APIResource.GSON.fromJson(rawJson, Event.class);
APIResource comes from their library (I am using 1.6.5)
In order to abstract all of the deserialization logic out of the controller I did the following:
Created a custom deserializer
public class StripeEventDeserializer extends JsonDeserializer<Event> {
private ObjectMapper mapper;
public StripeEventDeserializer(ObjectMapper mapper) {
this.mapper = mapper;
}
#Override
public Event deserialize(JsonParser jp, DeserializationContext context) throws IOException {
ObjectNode root = mapper.readTree(jp);
Event event = ApiResource.GSON.fromJson(root.toString(), Event.class);
return event;
}
}
I then needed to add that deserializer to my ObjectMapper config:
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Event.class, new StripeEventDeserializer(mapper));
mapper.registerModule(simpleModule);
I could then use #RequestBody correctly on the Spring rest controller:
#PostMapping("/webhook")
public void webhook(#RequestBody Event stripeEvent)

Categories

Resources