I'm trying to add the principal id to every incoming request as part of the body. This is the code I am using:
InputStream in = containerRequestContext.getEntityStream();
String jsonRequestString = IOUtils.toString(in, encoding);
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
JsonParser parser = factory.createParser(jsonRequestString);
JsonNode jsonNode = mapper.readTree(parser);
((ObjectNode) jsonNode).put(PRINCIPAL_ID, containerRequestContext.getSecurityContext().getUserPrincipal().getName());
containerRequestContext
.setEntityStream(IOUtils.toInputStream(mapper.writeValueAsString(jsonNode), encoding));
This is my resource:
#POST
#Path("/service")
#RolesAllowed(USER_ROLE)
#Produces(MediaType.APPLICATION_JSON)
public Response myService(String principalId, String input){
// do stuff
}
When request hits the resource, I can see the new json string being bind on principalId argument e.g.
principalId = {"principalId": "id", "input":"input"}
is there a way to bind each property to the respective argument?
You have two options here:
write interceptor (with AspectJ, for instance) which handle all your endpoints and populates principalId to your method's parameter or POJO.
write a servlet filter. This option is required to read the same request twice. The first read will occur in the filter with your following preprocessing and the second one in the Spring servlet.
Related
I need to get the list of all the films.
i am in this situation and i dont know how to manage it.
My project is divided in two smaller project. Back-end project and front-end project.
Back-end part that produce a Json that contains a list of films.
The service has this pattern
#GET
#Produce(json) // here is a particular library and it funcion correctly.
List<Film> getAllFilms
The output calling this service has this pattern:
[{"title:abc","time": 5486448}, {....}, {....}]
At the Front-end project i am using Resteasy .
I have create a class service to call the back-end and to manage the response
List<Film> film= new ArrayList<>();
try{
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://localhost:8080/film");
Response response = target.request().get();
film= List<Film>) response.readEntity(Film.class);
I have an exception of this type:
javax.ws.rs.ProcessingException: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of FILM out of START_ARRAY token
Now i am trying to understand something but there is full of material and i am loosing around.
How can i unmarshall an array to a list ?
You can use readValue method of jackson to convert it to List
public List<Film> convert(String jsonString) throws JsonParseException, JsonMappingException,
IOException {
ObjectMapper objectMapper = new ObjectMapper();
List<Film> filmList = objectMapper.readValue(
jsonString,
objectMapper.getTypeFactory().constructCollectionType(
List.class, Film.class));
return filmList;
}
I am using Jersey service and sending a json response from a service call. Now my service will be used from cross domain as well. so i want to implement CORS in my code. I saw few examples in internet but all are returning response object. But in my code i am returning as ObjectWritter. in this case how to make this as CORS.
My code as below
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/sampleUrl/{pageNumber}")
public String fetchAlertRecords(#PathParam("pageNumber") int pageNumber) throws Exception {
List<CustomVO> list = new ArrayList<CustomVO>();
//do somethimg to insert values to list
ObjectWriter writer = null;
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
FilterProvider filters = new SimpleFilterProvider()
.addFilter(filterClass,SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
writer = mapper.writer(filters);
return writer.writeValueAsString(list);
}
CORS is about request/response headers, what you return (btw you return String and not ObjectWriter) is a response body. So use CORS as you have seen it in examples. May be this link will help https://spring.io/guides/gs/rest-service-cors/
I just learn about spring mvc for few days and there are some codes I wrote :
#RequestMapping(value = "/login", method = {RequestMethod.POST, RequestMethod.GET}, consumes = "*/*", produces = "application/json")
#ResponseBody
public Object userLogin(#RequestParam(value = "userName") String userName,#RequestParam(value = "password") String password) {
String password2 = userService.selectUserPassword(userName);
JSONObject object = new JSONObject();
if(password2.equals(password)){
object.put("login", "true");
}else{
object.put("login", "false");
}
return object;
}
It doesn't work, but if I change the code to this
public Map<String, String> userLogin(#RequestParam(value = "userName") String userName,
#RequestParam(value = "password") String password) {
String password2 = userService.selectUserPassword(userName);
Map<String, String> map = new HashMap<String, String>(1);
//JSONObject object = new JSONObject();
if(password2.equals(password)){
map.put("login", "true");
}else{
map.put("login", "false");
}
return map;
}
then it works. So I was wondering why?
To understand a difference between your two methods, note the following
The #ResponseBody annotation indicates that the return type is written to the response body.
Spring converts the returned object to a response body by using an appropriate HttpMessageConverter
Since you've indicated the produces = "application/json" the converter that will kick in is MappingJackson2HttpMessageConverter An HttpMessageConverter implementation that can read and write JSON using Jackson’s ObjectMapper
this is a powerfull feature that Spring MVC does automatically, it resolve the representation, and converts automatically.
In your first method you've attempted doing this manually which is not needed and wrong. What the framework attempted is to convert your JsonObject to JSON representation, which either fails or gives you JSON properties containing the fileds of the JsonObject class.
In the later method you poplated the values to a Map, and let the Spring MVC convert to JSON. As Jackson libraries knows how to convert the Map to JSON, your later method works without issues
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I'm invoking a rest service that returns JSON like this:
{
"some.key" : "some value",
"another.key" : "another value"
}
I would like to think that I can invoke this service with a java.util.Map as the response type but that's not working for me. I get this exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map]
Should I just specify String as the response type and convert the JSON to a Map?
Edit I
Here's my restTemplate call:
private Map<String, String> getBuildInfo(String buildUrl) {
return restTemplate.getForObject(buildUrl, Map.class);
}
Here's how I'm setting up the restTemplate:
#PostConstruct
public void initialize() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
requestWrapper.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
return execution.execute(requestWrapper, body);
}
});
restTemplate.setInterceptors(interceptors);
}
Edit II
Full error message:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:549) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239) ~[spring-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at idexx.ordering.services.AwsServersServiceImpl.getBuildInfo(AwsServersServiceImpl.java:96) ~[classes/:na]
RestTemplate has a method named exchange that takes an instance of ParameterizedTypeReference as parameter.
To make a GET request that returns a java.util.Map, just create an instance of an anonym class that inherits from ParameterizedTypeReference.
ParameterizedTypeReference<Map<String, String>> responseType =
new ParameterizedTypeReference<>() {};
You can then invoke the exchange method:
RequestEntity<Void> request = RequestEntity.get("http://example.com/foo")
.accept(MediaType.APPLICATION_JSON).build();
Map<String, String> jsonDictionary = restTemplate.exchange(request, responseType).getBody();
As I had previously noted, your error message is showing us that you are receiving application/octet-stream as a Content-Type.
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [interface java.util.Map] and content type [application/octet-stream]
As such, Jackson's MappingJackson2HttpMessageConverter cannot parse the content (it's expecting application/json).
Original answer:
Assuming your HTTP response's Content-Type is application/json and you have have Jackson 1 or 2 on the classpath, a RestTemplate can deserialize JSON like you have into a java.util.Map just fine.
With the error you are getting, which you haven't shown in full, either you've registered custom HttpMessageConverter objects which overwrite the defaults ones, or you don't have Jackson on your classpath and the MappingJackson2HttpMessageConverter isn't registered (which would do the deserialization) or you aren't receiving application/json.
I think you can achieve what you're aiming for simply using the RestTemplate and specifying a JsonNode as the response type.
ResponseEntity<JsonNode> response =
restTemplate.exchange(url, HttpMethod.GET, entity, JsonNode.class);
JsonNode map = response.getBody();
String someValue = map.get("someValue").asText();
Update 02/05/2018 (about 4 years later)...I tested this again as people have been upvoting my question/answer and Sotirios Delimanolis is correct that I should not have to write the code in my answer to make this work. I used basically the same RestTemplate/REST service setup as shown in my question with the REST service having a confirmed response content type of application/json and RestTemplate was able to process the response with no issues into a Map.
I ended up getting the contents as a String and then converting them to a Map like this:
String json = restTemplate.getForObject(buildUrl, String.class);
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
try {
//convert JSON string to Map
map = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});
} catch (Exception e) {
logger.info("Exception converting {} to map", json, e);
}
return map;
I know its old, but just for other people that may visit this topic:
If you want to register some additional converters with RestTemplateBuilder you also have to explicitly register default ones
#Bean
public RestTemplateBuilder builder() {
return new RestTemplateBuilder()
.defaultMessageConverters()
.additionalMessageConverters(halMessageConverter());
}
private HttpMessageConverter halMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jackson2HalModule());
TypeConstrainedMappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
halConverter.setSupportedMediaTypes(Collections.singletonList(MediaTypes.HAL_JSON));
halConverter.setObjectMapper(objectMapper);
return halConverter;
}
This worked 100% for me
in client
Map<String, Object> mapRespuesta = new HashMap<>();
mapRespuesta.put("mensaje", "Process completed successfully");
mapRespuesta.put("idResponse", id);
return new ResponseEntity<Map<String, Object>>(mapRespuesta, HttpStatus.OK);
in which it makes the connection
ResponseEntity<Map> result = restTemplate.postForEntity(url, params, Map.class);
String id = result.getBody().get("idResponse").toString();
#GetMapping(value = "getSunny/{userId}")
public Map<String, SunnyVO> getSunny(#PathVariable int sunnyId) {
Map<String, SunnyVO> newObj = new HashMap<String, SunnyVO>();
final String url = "http://localhost:8085/Sunny/getSunny/{sunnyId}";
RestTemplate restTemplate = new RestTemplate();
newObj = restTemplate.getForObject(url, Map.class, sunnyId);
return newObj;
}
It is working for me ...
I write my API documentation with Spring REST Docs.
Code example:
#Override
public void getById(String urlTemplate, PathParametersSnippet pathParametersSnippet, Object... urlVariables) throws Exception {
resultActions = mockMvc.perform(get(urlTemplate, urlVariables)
.principal(principal)
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
// do..
}
But the problem is that the result of the test is answered in one line. And understanding the structure of the returned data is very difficult.
Response example:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"creator":null,"modifier":null,"modificationTime":null,"creationTime":null,"id":100,"deleted":false,"name":"Name","description":null,"report":[{"creator":"System","modifier":"System","modificationTime":"2019-01-30T14:21:50","creationTime":"2019-01-30T14:21:50","id":1,"name":"Form name","reportType":{"creator":"System","modifier":"System","modificationTime":"2019-01-30T14:21:50","creationTime":"2019-01-30T14:21:50","id":1,"deleted":false,"name":"Raport"},"unmodifiable":true}]}
Forwarded URL = null
Redirected URL = null
Cookies = []
Further, I generate documentation from the answer received and in the documentation also unformatted JSON
What am I doing wrong? How to enable formatting for json?
If you're not in a position to configure your application to produce pretty-printed responses, you can have REST Docs do it for you prior to them being documented. This is described in the Customizing Requests and Responses section of the documentation:
Preprocessing is configured by calling document with an OperationRequestPreprocessor, and/or an OperationResponsePreprocessor. Instances can be obtained using the static preprocessRequest and preprocessResponse methods on Preprocessors. For example:
this.mockMvc.perform(get("/")).andExpect(status().isOk())
.andDo(document("index", preprocessRequest(removeHeaders("Foo")),
preprocessResponse(prettyPrint())));
In the case above the request is being preprocessed to remove a Foo header and the response is being preprocessed so that it appears pretty-printed.
You can try get ResultActions object from mockMvc and than get MockHttpServletResponse object. After that you can get all the values of the fields that came in response. In this case, you will not need to parse the string.
resultActions = mockMvc.perform(get(urlTemplate, urlVariables)
.principal(principal)
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
MockHttpServletResponse content = resultActions.andReturn().getResponse();
Also you can transform MockHttpServletResponse object data to json. IUf you use Jacson, than write your custom serializer for this object, add it to MockHttpServletResponse and register in ObjectMapper.
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(MockHttpServletResponse.class, CustomSerializer.class);
mapper.registerModule(module);
String jsonResult = mapper.writeValueAsString(content);
CustomSerializer should extends StdSerializer<MockHttpServletResponse> and override serialize method.