Spring Boot controller to forward general json - json

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

Related

Spring Boot REST multi part request (file + json) throwing 415 Unsupported Media Type exception

I am using spring boot version = 1.5.2.RELEASE.
When I am sending multi part file with json object to upload file in postman, It throwing 415 Unsupported Media Type exception.
This is my controller class.
#RestController
#RequestMapping("/service/promotion/")
public class JobController {
....
....
....
#RequestMapping(value = "/job/test", method = RequestMethod.POST, produces = "application/json", consumes = "multipart/form-data")
public ResponseEntity<Object> createJobTest(#Valid #RequestBody JobRequest jobRequest,
#RequestParam(value = "file", required = false) MultipartFile multiPartFile) throws Exception {
My json request class.
public class JobRequest {
private String campaignKey;
private String communicationId;
private Integer channelId;
private String templateType;
private String subject;
private String frequencyControl;
private Integer leadsRequested;
private String keywordRelavance;
private String scheduledAt;
private String file;
private String updatedBy;
//getter and setter
}
Json request in postman
Multipart file request in postman
Header Content-type
But when I removed consumes from controller class and from postman as well like
#RequestMapping(value = "/job/test", method = RequestMethod.POST, produces = "application/json")
then debugger coming in controller class but multi part file value coming
null in request object like
I googled a lot there are many similar questions which already posted but none of them helped me.
Please help me to sort out this mystery.
Thank you.
Check this File upload along with other object in Jersey restful web service
Another way is you can pass whole object in text like you are passing file in form-data and convert in object.
#RequestMapping(value = "/uploadDocs", method = RequestMethod.POST, produces = "application/json", consumes = "multipart/form-data")
public ResponseEntity<Object> methodName(#RequestParam("files") MultipartFile file, #RequestParam("anyKeyName") String objectString)
Than you can convert string to object using
Class object = new ObjectMapper().readValue(objectString, Class.class);

How to avoid string escape in resttemplate postForObject

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.

Send POST and PUT through RestTemplate to a Spring Data Rest Api

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();
//

How to get JSON response from controller to another controller in Spring

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.

Backbone.js Collection.sync & spring endpoints

I have a backbone.js client app and Spring on the app tier. In Backbone I'm using a Collection of 'ServerConfig' objects... the URL of the backbone collection points to a spring endpoint.
The 'GET' request returns an ArrayList of a POJO called 'ServerRuntimeConfig' which ultimately ends up returning a JSON response with the following response body:
[{"useIUX":true,"serverDomainName":"localhost","selected":true}]
The 'PUT' request, which is called when I do a 'sync' on the collection sends the following JSON request with the below request body (same as the GET):
[{"useIUX":true,"serverDomainName":"localhost","selected":true}]
The problem is that I can't get spring to automatically map the JSON collection into a collection of POJOs on the PUT... so I've had to do it manually.
Here is the GET end point
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.GET,
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigRequest(
HttpServletRequest request,
HttpServletResponse response) {
// first grab all of the hostnames from the properties file
ArrayList<String> allServersDomains = new ArrayList();
List<String> servers = Arrays.asList(
StringUtils.tokenizeToStringArray(
this.getAppProperties().get("serverList"), ","));
allServersDomains.addAll(servers);
MyTTJMXClient jmxClient = new MyTTJMXClient();
return jmxClient.readRemoteConfigurations(allServersDomains);
}
Here is the PUT endpoint
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.PUT,
consumes = "application/json",
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
#RequestBody String body,
HttpServletRequest request,
HttpServletResponse response) {
String jsonSource = body;
ObjectMapper mapper = new ObjectMapper();
ArrayList<ServerRuntimeConfig> serverConfigs = new ArrayList();
try {
serverConfigs = mapper.readValue(jsonSource,
new TypeReference<ArrayList<ServerRuntimeConfig>>() { } );
} catch (IOException e) {
e.printStackTrace();
}
MyTTJMXClient jmxClient;
jmxClient = new MyTTJMXClient();
return jmxClient.setRemoteConfigurations(serverConfigs);
}
Is there a way to avoid the manual Jackson mapping I'm doing in the PUT endpoint? I tried the following endpoint signature but serverConfigs is always null
#RequestMapping(value = "/runtimeConfigs",
method = RequestMethod.PUT,
consumes = "application/json",
produces = "application/json")
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
Again, the manual Jackson mapping does the trick, but it's not using the awesomeness that is Spring.
Has anyone else out there built a Spring end point that automatically maps Backbone/JSON collections to POJOs?
Assuming the mapping you've used in your PUT method works,
Try to change your method signature from:
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
to:
#ResponseBody
public ArrayList<ServerRuntimeConfig> handleConfigUpdate(
#RequestBody ArrayList<ServerRuntimeConfig> serverConfigs,
HttpServletRequest request,
HttpServletResponse response) {
The #RequestBody annotation is the important part here. It will automatically use spring's converter (in this case, jackson) to populate the variable.