Having a client should send plain json string to a RESTful service:
...
final Gson gson = new GsonBuilder().create();
final String payload = gson.toJson(data);
final RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
final HttpEntity<String> entity = new HttpEntity<>(payload, headers);
restTemplate.postForObject("http://localhost:8080/data/bulk", entity, Void.class);
...
The produced json by GSON looks like:
{ "id" : { "poid" : "5b70cabhsdf66d99sdakfj37e45" } ... }
The REST service now is receiving the request:
#RequestMapping(value = "/data/bulk", method = RequestMethod.POST)
public ResponseEntity<Void> bulkInbound(#RequestBody final String bulkjson) {
But the string in request body which should be exactly the same as the produced json looks like:
{ \"id\" : { \"poid\" : \"5b70cabhsdf66d99sdakfj37e45\" } ... }
So the string in the body is escaped which makes some problems.
Sending the same json string via POSTMAN ist works like a charme without escaping.
How can i tell resttemplate in my client not to escape my string?
For whoever still encounters this... took me hours to find it:
Adding StringHttpMessageConverter as the first converter fixed the escaping issue and still using GSON for serializing (ie gson.toJson(data)). With this change the JSON was posted with double-quotes unescaped.
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new StringHttpMessageConverter());
converters.add(new GsonHttpMessageConverter());
restTemplate.setMessageConverters(converters);
So, after spending much time on research i think it is the best way to use JSONObject for that. My solution looks like:
...
final JSONObject jsonobject = new JSONObject(data);
final RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
final HttpEntity<String> entity = new HttpEntity<>(jsonobject.toString(), headers);
restTemplate.postForObject("http://localhost:8080/data/bulk", entity, Void.class);
...
This works for me and solves my problem with String escaping.
Related
I want to receive a JSONObject which I will later convert to my own Object within Spring. This is my controller code where objJson has null values:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, value ="postObj")
public void postObj(#RequestBody JSONObject objJson){
objCompleteService.insertObj(objJson);
}
I'm sending this with postman:
{"obj":"Test","objId":0,"definition":"post","languageId":0,"meaning":"post","submitterId":0}
and have set the Content-Type to application/json; charset=UTF-8
objJson is coming in as null.
You will get JSON as map. Paylod is the whole JSON object. You can deserialize the object as you like. if you are sending raw data use #RequestBody but if you are sending form data use #RequestParam
#PostMapping(value = "/postObj")
#Timed
public void postObj(#RequestBody Map<String, Object> payload){
JSONObject jsonObject = new JSONObject(new ObjectMapper().writeValueAsString(payload));
}
I've been developing a cloud app to mess a little with Spring Cloud and such. Now I'm stuck trying to send a POST or a PUT request to a Spring Data Rest backend using the RestTemplate API but everything I tried ends with an error: HttpMessageNotReadableException: Cannot deserialize instance of java.lang.String out of START_OBJECT token, HttpMessageNotReadableException: Could not read document: Can not deserialize instance of java.lang.String out of START_ARRAY token, ...from request with content type of application/xml;charset=UTF-8!, Error 400 null... you name it. After researching I discovered that it actually is quite hard to consume HAL JSON with RestTemplate (level 3 JSON hypermedia if I recall correctly) but I want to know if it is possible.
I'd like to see some working (detailed if possible) examples of a RestTemplate sending POST and PUT to a Spring Data Rest backend.
Edit: I tried postForEntity, postForLocation, exchange and it just ended in different kinds of errors. Those are some snippets I tried (there're more, it's just that I dispose them).
My entity:
#Entity
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#NotNull
#NotEmpty
private String username;
#NotNull
#NotEmpty
private String authorities;
#NotNull
#NotEmpty
private String password;
//Constructor, getter and setter
Some restTemplate attemps:
public Account create(Account account) {
//Doesnt work :S
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("name", account.getName());
map.add("username", account.getUsername());
map.add("password", account.getPassword());
map.add("authorities", account.getAuthorities());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
final HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<MultiValueMap<String, String>>(map,
headers);
return restTemplate.exchange(serviceUrl + "/accounts", HttpMethod.POST, entity, Account.class).getBody();
}
//Also tried with a AccountResource which extends from ResourceSupport and doesn't work either. This one gives me a error saying it cannot deserialize Account["name"].
Also tried like this and got an error about header being application/xml: RestTemplate POSTing entity with associations to Spring Data REST server
The other ones just repeat one of those errors.
You need to configure your RestTemplate so it can consume the application/hal+json content type.
It has already been addressed in some other posts, such as this one or that one, and on a bunch of blog posts, such as here.
The following solution works for a Spring Boot project:
First, configure your RestTemplate using a bean:
// other import directives omitted for the sake of brevity
import static org.springframework.hateoas.MediaTypes.HAL_JSON;
#Configuration
public class RestTemplateConfiguration {
#Autowired
private ObjectMapper objectMapper;
/**
*
* #return a {#link RestTemplate} with a HAL converter
*/
#Bean
public RestTemplate restTemplate() {
// converter
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
converter.setObjectMapper(objectMapper);
RestTemplate restTemplate = new RestTemplate(Collections.singletonList(converter));
return restTemplate;
}
}
Then, let Spring inject the RestTemplate where you need to consume the REST backend, and use one of the many variants of RestTemplate#exchange:
#Autowired
public RestTemplate restTemplate;
...
// for a single ressource
// GET
Account newAccount = restTemplate.getForObject(url, Account.class);
// POST
Account newAccount = restTemplate.exchange(serviceUrl + "/accounts", HttpMethod.POST, entity, Account.class).getBody();
// or any of the specialized POST methods...
Account newAccount = restTemplate.postForObject(serviceUrl + "/accounts", entity, Account.class);
For a collection, you will manipulate a PagedResources
// for a collection
ParameterizedTypeReference<PagedResources<Account>> responseType =
new ParameterizedTypeReference<PagedResources<Account>>() {};
// GET
PagedResources<Account> accounts =
restTemplate.exchange(url, HttpMethod.GET, null, responseType).getBody();
//
In a Spring Boot controller, I am receiving json and want to "forward" it without any processing:
#RequestMapping(value = "/forward", method = RequestMethod.POST)
public void abc(#RequestBody GeneralJsonRepresentation json, HttpServletRequest request) {
restTemplate.postForEntity(endpoint, json, Object.class)
}
Is it possible to accomplish this, for instance with an implementation of GeneralJsonRepresentation, assuming the controller has no knowledge of the json format and that the received content type is application/json?
You may not even need the GeneralJsonRepresentation if you just use a String.
I created a small working snippet:
#RequestMapping(path="/forward", method = RequestMethod.POST)
public ResponseEntity<String> forward(#RequestBody String postData) {
// maybe needed configuration
final RestTemplate restTemplate = new RestTemplateBuilder().basicAuthorization("user", "password").build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(postData, headers);
final String targetUrl = "http://targethost/endpoint";
final ResponseEntity<String> response = restTemplate.postForEntity(targetUrl, entity, String.class);
return ResponseEntity.created(...).build();
}
I have one controller in an app that returns JSON data, like so:
#RequestMapping(value = "/{number}", method = RequestMethod.GET)
#ResponseBody
public String number(
HttpServletRequest request,
HttpServletResponse response,
#PathVariable int number
) {
JSONObject dataObject = new JSONObject();
dataObject.put("firstName", "Sheelten");
dataObject.put("lastName", "Pestay");
JSONArray data = new JSONArray();
data.put(dataObject);
return data.toString();
}
I have another controller on a different app, that I want to receive the JSON data, like so:
#RequestMapping(
value = "/data/test/",
method = RequestMethod.GET
)
#ResponseBody
public String testService(
HttpServletRequest request,
HttpServletResponse response,
Model model
) {
return response.toString();
}
I'm not really sure how I would go about receiving the JSON data into my testService controller method. I've tried googling and using the response object with no luck.
Anyone have an idea how I'd do this?
Found an answer, in case anyone else is ever looking for a solution to this.
If you are using Java with Spring, use the RestTemplate class. See below:
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("http://yoururl/here", String.class);
The string result will be your JSON string.
Just see the code snippet of SpringMVC-3.2.x controller action method. Its quite easy to generate JSON but unable to add addtional custom header only for this action/specific action method for specific controller. not common for all JSON #ResponseBody action method .
#RequestMapping(value="ajaxDenied", method = RequestMethod.GET)
public #ResponseBody Map<String, Object> ajaxDenied(ModelMap model) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("severity", "error");
message.put("summary", "Restricted access only");
message.put("code", 200);
Map<String, Object> json = new HashMap<String, Object>();
json.put("success", false);
json.put("message", message);
return json;
}
In the different way I could add additional headers as my demand but here is some problem in generating pure JSON. Its generate buggy JSON and able to parse few browser.
#RequestMapping(value="ajaxSuccess", method = RequestMethod.GET)
public ResponseEntity<String> ajaxSuccess(){
Map<String, Object> message = new HashMap<String, Object>();
message.put("severity", "info");
message.put("location", "/");
message.put("summary", "Authenticated successfully.");
message.put("code", 200);
Map<String, Object> json = new HashMap<String, Object>();
json.put("success", true);
json.put("message", message);
String data = "";
try {
ObjectMapper mapper = new ObjectMapper();
data = mapper.writeValueAsString(json);
} catch (Exception e) { //TODO
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=UTF-8");
headers.add("X-Fsl-Location", "/");
headers.add("X-Fsl-Response-Code", "302");
return (new ResponseEntity<String>(data, headers, HttpStatus.OK));
}
this action method could generate JSON String with escape character rather than pure JSON so depend on browser how it will be parse, Its cause failure for chrome. The output just look like
"{\"message\":{\"summary\":\"Authenticated successfully.\",\"location\":\"/\",\"severity\":\"info\",\"code\":\"200\"},\"success\":true}"
but our desired output
{
"message":{
"summary": "Authenticated successfully.",
"location":"/",
"severity":"info",
"code":"200"
},
"success":true
}
I want to generate pure JSON with additional headers based on conditions for specific action of specific controller.
You can add headers to the ResponseEntity builder. I think it is cleaner this way.
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
// ...
#GetMapping("/my/endpoint")
public ResponseEntity myEndpointMethod() {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
return ResponseEntity.ok()
.headers(headers)
.body(data);
}
Here is the solution as the suggestion of M. Deinum
#RequestMapping(value="ajaxSuccess", method = RequestMethod.GET)
public ResponseEntity<Map<String, Object>> ajaxSuccess(){
Map<String, Object> message = new HashMap<String, Object>();
message.put("severity", "info");
message.put("location", "/");
message.put("summary", "Authenticated successfully.");
message.put("code", 200);
Map<String, Object> json = new HashMap<String, Object>();
json.put("success", true);
json.put("message", message);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=UTF-8");
headers.add("X-Fsl-Location", "/");
headers.add("X-Fsl-Response-Code", "302");
return (new ResponseEntity<Map<String, Object>>(json, headers, HttpStatus.OK));
}
You can also use HttpServletResponse for adding your status and headers in a more easy way:
#RequestMapping(value="ajaxSuccess", method = RequestMethod.GET)
#ResponseBody
public String ajaxSuccess(HttpServletResponse response) {
response.addHeader("header-name", "value");
response.setStatus(200);
return "Body";
}
Therefore you need to add following maven dependency as provided:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>7.0.53</version>
<scope>provided</scope>
</dependency>