Spring Boot: How to Add Params to TestRestTemplate.postForEntity? - java

I am trying to add parameters to a postForEntity request but it seems to never go through. Here is the minimum reproducible code:
#Test
public void test()
{
String urlTemplate = UriComponentsBuilder.fromHttpUrl("http://localhost:8080/test")
.queryParam("update")
// .queryParam("update", "{update}") //This does not work either
.encode()
.toUriString();
HashMap<String, String> paramValues = new HashMap<>();
paramValues.put("update", "true");
HttpEntity<AnimateRequest> httpEntity = new HttpEntity<>(null, new HttpHeaders());
ResponseEntity<Boolean> response = this.template.postForEntity(
urlTemplate,
httpEntity,
Boolean.class,
paramValues);
boolean bb = response.getBody();
}
In a controller:
#PostMapping(value = "/test")
public ResponseEntity<Boolean> tester(#RequestParam(name="update", required = false) boolean rr)
{
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(rr);
}
Errors with:
org.springframework.web.client.RestClientException: Error while extracting response for type [class java.lang.Boolean] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1]

I'm not exactly sure why, but the return contentType() in the controller return needed to be removed. For some reason Jackson was not parsing the return type properly. Then either a primitive boolean or the class Boolean works.

Main problem, is that your implementation tries to respond with text/plain without registering any Boolean to text/plain converter.
You have several options to solve that:
Just return (response with) the "default (media) type":
return ResponseEntity
.ok()
.body(rr);
If you need to respond with text/plain, then
a. ResponseEntity<String> would be the straight-forward solution:
#PostMapping(value = "/test2")
public ResponseEntity<String> // String!!! not Boolean ... {
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_PLAIN) // explicit media type here or as #PostMapping.produces attribute
.body(String.valueOf(rr)); // convert the boolean here
}
b. Or to really register a custom(ized) (Boolean<->text/plain) converter ...
Then we could test 1. (with TestRestTemplate) like:
#Test
public void test1() throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort + "/test/";
URI uri = new URI(baseUrl);
// true:
ResponseEntity<Boolean> result = this.restTemplate.postForEntity(uri + "?update=true", null /*here goes normally the "post body"/entity*/, Boolean.class);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
assertThat(result.getBody()).isTrue();
}
and 2. accordingly with string result:
#Test
public void test2() throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort + "/test2/";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = this.restTemplate.postForEntity(uri + "?update=true", null, String.class);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
assertThat(Boolean.valueOf(result.getBody())).isTrue();
}
Please consider, we have several (out-of-the-box) options regarding "content encoding" and "how to pass this update parameter".
For brevity, simplicity and lack of need I omit any post objects and headers (null, which would go as the second method argument), and passed the only parameter as "URI parameter".
Also consider the note on RestTemplate, which could also be applied to TerstRestTemplate:
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

Related

Spring - Unable To Send Error Message For API Returning ByteArrayResource

I have an rest API in a Spring for generating and downloading a PDF file. The controller definitation is as follows -
#RequestMapping(
value = "/foo/bar/pdf",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
#ResponseBody
#Nullable
public ByteArrayResource downloadPdf(#RequestParam int userId) {
byte[] result = null;
ByteArrayResource byteArrayResource = null;
result = service.generatePdf(userId);
if (result != null) {
byteArrayResource = new ByteArrayResource(result);
}
return byteArrayResource;
}
I use Jackson for JSON handling JSON and have an Exception handler ControllerAdvice. The problem is when this API generates an exception and I return a custom exception class (contains message and one additional field).
As I already specified produces = MediaType.APPLICATION_OCTET_STREAM_VALUE this custom class is also attempted to be converted to an octet stream by Spring, which it fails at and produces HttpMediaTypeNotAcceptableException: Could not find acceptable representation.
I tried solutions on this Stackoverflow question, particularly this answer but it still fails. This solution, along with other changes suggests removing produces part from #RequestMapping but when I debugged into AbstractMessageConverterMethodProcessor.getProducibleMediaTypes it only detects application/json as available response media type.
tl;dr
How can I have this API return the file on success and correctly return custom exception class's JSON representation on error.
I had the same problem with similar code. I just removed the produces attribute from my #PostMapping and I was able to return the file or the json (when the api have some error):
#Override
#PostMapping
public ResponseEntity<InputStreamResource> generate(
#PathVariable long id
) {
Result result = service.find(id);
return ResponseEntity
.ok()
.cacheControl(CacheControl.noCache())
.contentLength(result.getSize())
.contentType(MediaType.parseMediaType(MediaType.APPLICATION_PDF_VALUE))
.body(new InputStreamResource(result.getFile()));
}
When some error occur, I had a #ExceptionHandler to care of that:
#ExceptionHandler
public ResponseEntity<ApiErrorResponse> handleApiException(ApiException ex) {
ApiErrorResponse error = new ApiErrorResponse(ex);
return new ResponseEntity<>(error, ex.getHttpStatus());
}
Try implements your action as
#RequestMapping(
value = "/foo/bar/pdf",
method = RequestMethod.GET)
#ResponseBody
public HttpEntity<byte[]> downloadPdf(#RequestParam int userId) {
byte[] result = service.generatePdf(userId);
HttpHeaders headers = new HttpHeaders();
if (result != null) {
headers.setContentType(new MediaType("application", "pdf"));
headers.set("Content-Disposition", "inline; filename=export.pdf");
headers.setContentLength(result.length);
return new HttpEntity(result, headers);
}
return new HttpEntity<>(header)
}
About exception handling for example you may throw YourCustomError and in controller annotated with #ControllerAdvice annotate a method with #ExceptionHandler(YourCustomError.class) and work with it.

How to extract HTTP status code from the RestTemplate call to a URL?

I am using RestTemplate to make an HTTP call to our service which returns a simple JSON response. I don't need to parse that JSON at all. I just need to return whatever I am getting back from that service.
So I am mapping that to String.class and returning the actual JSON response as a string.
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return response;
Now the question is -
I am trying to extract HTTP Status codes after hitting the URL. How can I extract HTTP Status code from the above code? Do I need to make any change into that in the way I doing it currently?
Update:-
This is what I have tried and I am able to get the response back and status code as well. But do I always need to set HttpHeaders and Entity object like below I am doing it?
RestTemplate restTemplate = new RestTemplate();
//and do I need this JSON media type for my use case?
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
//set my entity
HttpEntity<Object> entity = new HttpEntity<Object>(headers);
ResponseEntity<String> out = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
System.out.println(out.getBody());
System.out.println(out.getStatusCode());
Couple of question - Do I need to have MediaType.APPLICATION_JSON as I am just making a call to url which returns a response back, it can return either JSON or XML or simple string.
Use the RestTemplate#exchange(..) methods that return a ResponseEntity. This gives you access to the status line and headers (and the body obviously).
getStatusCode()
getHeaders()
If you don´t want to leave the nice abstraction around RestTemplate.get/postForObject... methods behind like me and dislike to fiddle around with the boilerplate stuff needed when using RestTemplate.exchange... (Request- and ResponseEntity, HttpHeaders, etc), there´s another option to gain access to the HttpStatus codes.
Just surround the usual RestTemplate.get/postForObject... with a try/catch for org.springframework.web.client.HttpClientErrorException and org.springframework.web.client.HttpServerErrorException, like in this example:
try {
return restTemplate.postForObject("http://your.url.here", "YourRequestObjectForPostBodyHere", YourResponse.class);
} catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) {
if(HttpStatus.NOT_FOUND.equals(httpClientOrServerExc.getStatusCode())) {
// your handling of "NOT FOUND" here
// e.g. throw new RuntimeException("Your Error Message here", httpClientOrServerExc);
}
else {
// your handling of other errors here
}
The org.springframework.web.client.HttpServerErrorException is added here for the errors with a 50x.
Now you´re able to simple react to all the StatusCodes you want - except the appropriate one, that matches your HTTP method - like GET and 200, which won´t be handled as exception, as it is the matching one. But this should be straight forward, if you´re implementing/consuming RESTful services :)
If you want all the HTTPStatus from a RestTemplate including 4XX and 5XX, you will have to provide an ResponseErrorHandler to the restTemplate, since the default handler will throw an exception in case of 4XX or 5XX
We could do something like that :
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
#Override
public boolean hasError(HttpStatus statusCode) {
return false;
}
});
ResponseEntity<YourResponse> responseEntity =
restTemplate.getForEntity("http://your.url.here", YourResponse.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.XXXX);
private RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(url,HttpMethod.GET, requestEntity,String.class);
response contains 'body', 'headers' and 'statusCode'
to get statusCode : response.getStatusCode();
exchange(...) works but if you want less code, you can use
org.springframework.boot.test.web.client.TestRestTemplate.getForEntity(...)
which returns an Entity containing StatusCode. Change your example code to this:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
HttpStatus statusCode = response.getStatusCode();
To test it you can use this snippet from my unit test:
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
assertResponseHeaderIsCorrect(response, HttpStatus.OK);
/**
* Test the basics of the response, non-null, status expected, etc...
*/
private void assertResponseHeaderIsCorrect(ResponseEntity<String> response, HttpStatus expectedStatus) {
assertThat(response).isNotNull();
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON_UTF8);
assertThat(response.getStatusCode()).isEqualTo(expectedStatus);
}
There can be some slightly trickier use cases someone might fall in (as I did). Consider the following:
Supporting a Page object in order to use it with RestTemplate and ParameterizedTypeReference:
RestPageResponse:
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
public class RestResponsePage<T> extends PageImpl<T>{
private static final long serialVersionUID = 3248189030448292002L;
public RestResponsePage(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestResponsePage(List<T> content) {
super(content);
}
public RestResponsePage() {
super(new ArrayList<T>());
}
}
Using ParameterizedTypeReference will yield the following:
ParameterizedTypeReference<RestResponsePage<MyObject>> responseType =
new ParameterizedTypeReference<RestResponsePage<MyObject>>() {};
HttpEntity<RestResponsePage<MyObject>> response = restTemplate.exchange(oauthUrl, HttpMethod.GET, entity, responseType);
Calling #exchange:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<?> entity = new HttpEntity<>(headers);
response = restTemplate.exchange("localhost:8080/example", HttpMethod.GET, entity, responseType);
Now here is the "tricky" part.
Trying to call exchange's getStatusCode will be impossible because the compiler, unfortunately, will be unaware of the "intended" type of response.
That is because generics are implemented via type erasure which removes all information regarding generic types during compilation (read more - source)
((ResponseEntity<RestResponsePage<MyObject>>) response).getStatusCode()
In this case, you have to explicitly cast the variable to the desired Class to get the statusCode (and/or other attributes)!
Putting this much of code is enough for me
HttpStatus statusCode = ((ResponseEntity<Object>) responseOfEsoft).getStatusCode();
You can use this solution
RestTemplate restTemplate = new RestTemplate();
final String baseUrl = "http://www.myexampleurl.com";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = restTemplate.getForEntity(uri, String.class);
//get status code
int statuCode = result.getStatusCodeValue();
Was able to solve this through:
HttpEntity<Object> entity = restTemplate.getForEntity(uri, Object.class);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println(result.getStatusCode());

Why RestTemplate GET response is in JSON when should be in XML?

I struggled with an extrange spring behavior using RestTemplate (org.springframework.web.client.RestTemplate) without success.
I use in my hole application below code and always receive an XML response, which I parse and evaluate its result.
String apiResponse = getRestTemplate().postForObject(url, body, String.class);
But can't figure out why a server response is in JSON format after executing:
String apiResponse = getRestTemplate().getForObject(url, String.class);
I've debugged at low level RestTemplate and the content type is XML, but have no idea why the result is in JSON.
When I access from a browser the response is also in XML, but in apiResponse I got JSON.
I tried many options after reading Spring documentation
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/web/client/RestTemplate.html
Also tried to modify explicitly the headers but still can't figure it out.
I debugged RestTemplate class and noticed that this method is always setting application/json:
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (responseType != null) {
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter.canRead(responseType, null)) {
List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharSet() != null) {
supportedMediaType =
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
}
allSupportedMediaTypes.add(supportedMediaType);
}
}
}
if (!allSupportedMediaTypes.isEmpty()) {
MediaType.sortBySpecificity(allSupportedMediaTypes);
if (logger.isDebugEnabled()) {
logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
}
Could you give an idea?
I could solve my issue with RC.'s help. I'll post the answer to help other people.
The problem was that Accept header is automatically set to APPLICATION/JSON so I had to change the way to invoke the service in order to provide the Accept header I want.
I changed this:
String response = getRestTemplate().getForObject(url, String.class);
To this in order to make the application work:
// Set XML content type explicitly to force response in XML (If not spring gets response in JSON)
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = getRestTemplate().exchange(url, HttpMethod.GET, entity, String.class);
String responseBody = response.getBody();

How to check String in response body with mockMvc

I have simple integration test
#Test
public void shouldReturnErrorMessageToAdminWhenCreatingUserWithUsedUserName() throws Exception {
mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
.content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(?);
}
In last line I want to compare string received in response body to expected string
And in response I get:
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {Content-Type=[application/json]}
Content type = application/json
Body = "Username already taken"
Forwarded URL = null
Redirected URL = null
Tried some tricks with content(), body() but nothing worked.
You can call andReturn() and use the returned MvcResult object to get the content as a String.
See below:
MvcResult result = mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
.content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isBadRequest())
.andReturn();
String content = result.getResponse().getContentAsString();
// do what you will
#Sotirios Delimanolis answer do the job however I was looking for comparing strings within this mockMvc assertion
So here it is
.andExpect(content().string("\"Username already taken - please try with different username\""));
Of course my assertion fail:
java.lang.AssertionError: Response content expected:
<"Username already taken - please try with different username"> but was:<"Something gone wrong">
because:
MockHttpServletResponse:
Body = "Something gone wrong"
So this is proof that it works!
Spring MockMvc now has direct support for JSON. So you just say:
.andExpect(content().json("{'message':'ok'}"));
and unlike string comparison, it will say something like "missing field xyz" or "message Expected 'ok' got 'nok'.
This method was introduced in Spring 4.1.
Reading these answers, I can see a lot relating to Spring version 4.x, I am using version 3.2.0 for various reasons. So things like json support straight from the content() is not possible.
I found that using MockMvcResultMatchers.jsonPath is really easy and works a treat. Here is an example testing a post method.
The bonus with this solution is that you're still matching on attributes, not relying on full json string comparisons.
(Using org.springframework.test.web.servlet.result.MockMvcResultMatchers)
String expectedData = "some value";
mockMvc.perform(post("/endPoint")
.contentType(MediaType.APPLICATION_JSON)
.content(mockRequestBodyAsString.getBytes()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.data").value(expectedData));
The request body was just a json string, which you can easily load from a real json mock data file if you wanted, but I didnt include that here as it would have deviated from the question.
The actual json returned would have looked like this:
{
"data":"some value"
}
Taken from spring's tutorial
mockMvc.perform(get("/" + userName + "/bookmarks/"
+ this.bookmarkList.get(0).getId()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is(this.bookmarkList.get(0).getId().intValue())))
.andExpect(jsonPath("$.uri", is("http://bookmark.com/1/" + userName)))
.andExpect(jsonPath("$.description", is("A description")));
is is available from import static org.hamcrest.Matchers.*;
jsonPath is available from import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
and jsonPath reference can be found here
Spring security's #WithMockUser and hamcrest's containsString matcher makes for a simple and elegant solution:
#Test
#WithMockUser(roles = "USER")
public void loginWithRoleUserThenExpectUserSpecificContent() throws Exception {
mockMvc.perform(get("/index"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("This content is only shown to users.")));
}
More examples on github
here a more elegant way
mockMvc.perform(post("/retrieve?page=1&countReg=999999")
.header("Authorization", "Bearer " + validToken))
.andExpect(status().isOk())
.andExpect(content().string(containsString("regCount")));
One possible approach is to simply include gson dependency:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
and parse the value to make your verifications:
#RunWith(SpringRunner.class)
#WebMvcTest(HelloController.class)
public class HelloControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HelloService helloService;
#Before
public void before() {
Mockito.when(helloService.message()).thenReturn("hello world!");
}
#Test
public void testMessage() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
String responseBody = mvcResult.getResponse().getContentAsString();
ResponseDto responseDto
= new Gson().fromJson(responseBody, ResponseDto.class);
Assertions.assertThat(responseDto.message).isEqualTo("hello world!");
}
}
Here is an example how to parse JSON response and even how to send a request with a bean in JSON form:
#Autowired
protected MockMvc mvc;
private static final ObjectMapper MAPPER = new ObjectMapper()
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerModule(new JavaTimeModule());
public static String requestBody(Object request) {
try {
return MAPPER.writeValueAsString(request);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseResponse(MvcResult result, Class<T> responseClass) {
try {
String contentAsString = result.getResponse().getContentAsString();
return MAPPER.readValue(contentAsString, responseClass);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Test
public void testUpdate() {
Book book = new Book();
book.setTitle("1984");
book.setAuthor("Orwell");
MvcResult requestResult = mvc.perform(post("http://example.com/book/")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody(book)))
.andExpect(status().isOk())
.andReturn();
UpdateBookResponse updateBookResponse = parseResponse(requestResult, UpdateBookResponse.class);
assertEquals("1984", updateBookResponse.getTitle());
assertEquals("Orwell", updateBookResponse.getAuthor());
}
As you can see here the Book is a request DTO and the UpdateBookResponse is a response object parsed from JSON. You may want to change the Jackson's ObjectMapper configuration.
Another option is:
when:
def response = mockMvc.perform(
get('/path/to/api')
.header("Content-Type", "application/json"))
then:
response.andExpect(status().isOk())
response.andReturn().getResponse().getContentAsString() == "what you expect"
You can use getContentAsString method to get the response data as string.
String payload = "....";
String apiToTest = "....";
MvcResult mvcResult = mockMvc.
perform(post(apiToTest).
content(payload).
contentType(MediaType.APPLICATION_JSON)).
andReturn();
String responseData = mvcResult.getResponse().getContentAsString();
You can refer this link for test application.
String body = mockMvc.perform(bla... bla).andReturn().getResolvedException().getMessage()
This should give you the body of the response. "Username already taken" in your case.
Another example is:
.andExpect(jsonPath("$").value(containsString("You have successfully deleted")));
The body response:
Body = You have successfully deleted a [Object] with ID: 1
Here is a more production ready way of doing it where you if you may have big json responses then you do not have to clutter your test files with json strings, just load them from static Resources folder and assert them directly.
#Test
#DisplayName("invalid fields")
void invalidfields() throws Exception {
String request = getResourceFileAsString("test-data/http-request/invalid-fields.json");
String response_file_path = "test-data/http-response/error-messages/invalid-fields.json";
String expected_response = getResourceFileAsString(response_file_path);
mockMvc.perform(evaluateRulesOnData(TRACKING_ID.toString(), request))
.andExpect(status().isBadRequest())
.andExpect(content().json(expected_response));
}
helper function to load test files from classpath
public static String getResourceFileAsString(String fileName) throws IOException {
Resource resource = new ClassPathResource(fileName);
File file = resource.getFile();
return new String(Files.readAllBytes(file.toPath()));
}
The expected response has an array with many elements in the list which are matched despite being in random order during each test run.

RestFul service issue

Trying to setup a restful service component that update a database table. Tried using both Spring RestTemplate as well as apache commons restful impl and both seems to no work.
On using
Option 1: Using Spring RestTemplate : Results in following error
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token
Option 2: Using using org.apache.commons.httpclient.methods.PostMethod; results in following errors
Server side error:
org.codehaus.jackson.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
Client side error:
The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
My Restful service method is annotated as "Post" and consumes "JSON". My client side controller which initiates the RestFul call, code below
#RequestMapping(value="/update", consumes="application/json")
public void updateMaintReport(
#RequestBody Map<String, String> formModelData,
HttpServletRequest request,
HttpServletResponse response)
throws IOException,JsonMappingException {
logger.log(LogLevel.DEBUG, "REACHED method updateMaintReport..");
System.out.println("Reached method updateMaintReport.....");
boolean errorEncountered = false;
ReportingSession repSession = null;
HttpSession session = request.getSession(false);
if(session==null) {
// TODO: code for handling invalid/expired session
} else {
repSession = (ReportingSession)session.getAttribute(ReportingWebConstants.REPORTING_SESSION);
if(repSession==null) {
errorEncountered = true;
}
}
if(!errorEncountered) {
ServiceClient serviceClient = new ServiceClient();
String servicesUrl = this.environment.getProperty("service_url_reports_data");
String servicesName = this.environment.getProperty("service_name_reports_update_fnol");
String serviceUrl = VIPUrlFactory.getServiceUrl(servicesUrl+servicesName);
logger.log(LogLevel.DEBUG, "For update : serviceUrl: "+serviceUrl);
//Option 1: Using Spring RestTemplate :
LinkedMultiValueMap<String,String> headers = new LinkedMultiValueMap<String,String>();
headers.add("Accept","application/json");
headers.add("Content-type","application/json");
List list = new ArrayList<Map<String, String>>(); list.add(formModelData);
RestTemplate restTemplate = new RestTemplate();
HttpEntity<List> requestEntity = new HttpEntity<List>(list, headers);
ResponseEntity<List> fList = restTemplate.exchange(serviceUrl,
HttpMethod.POST,
requestEntity,
List.class);
//Option 2: using org.apache.commons.httpclient.methods.PostMethod; -- Will be commented when option 1 block is uncommented
serviceClient.setParams(formModelData);
serviceClient.setServiceUrl(serviceUrl);
serviceClient.callRestServicePost();
logger.log(LogLevel.DEBUG, "Posting data to service - to execute the update");
}
}
In the above code, option 1 and option 2 block won't be executed simultaneously.
Below is the code block which accepts the Restful call, my server side code.
#RequestMapping(value = "/update", method = RequestMethod.POST)
public void updateMainRptData(#RequestBody Map<String, String> formModelData) throws ReportingIntegrationException,
IOException, JsonMappingException {
String updateStmt = "UPDATE CL_SCRIPTS SET DELETE_IND = #{delete_ind}, SCRIPT_DESC = #{script_desc}, SCRIPT_TXT = #{script_txt}WHERE COMPANY_CD = #{company_cd} AND SCRIPT_NAME = #{script_name}AND PROMPT_ID = #{prompt_id}";
ParameterObjectDTO paramObjDTO = new ParameterObjectDTO();
logger.log(LogLevel.DEBUG,"In Services Web: updateMainRptData()");
if(!formModelData.isEmpty()) {
Set<String> keySet = formModelData.keySet();
StringBuilder sb = new StringBuilder();
for (String key : keySet) {
sb.append(key).append(" -- ").append(formModelData.get(key)).append("\n");
}
logger.log(LogLevel.DEBUG, sb.toString());
}
paramObjDTO.setModalForQuery(formModelData);
paramObjDTO.setUpdateSqlStmt(updateStmt);
maintReportingSvc.updateMaintReport(paramObjDTO);
}
Error Messages I see in browsers is not helpful but my JSON data is valid I believe. Any help is appreciated. Thanks.
Later I changed the signature of the method updateMainRptData and added a returntype and #ResponseBody to resolve this issue.

Categories

Resources