How to POST data with Spring-boot RestTemplate - spring

Actually, i have this Rest Template request:
#RequestMapping(value = "/uploadProperties", method = RequestMethod.POST)
public #ResponseBody RessourceMetadata uploadProperties(
#RequestParam(value = "group", required = true) String group,
#RequestParam(value = "id", required = true) String id,
#RequestParam(value = "version", required = true) String version,
#RequestParam(value = "env", required = true) String env) {
try {
Ressource ressource = new Ressource(content, group, id, version, env, PropertiesFileUtils.getPropertiesFilename());
getRessourceService().save(ressource);
return ressource.getMetadata();
} catch (RuntimeException e) {
log.error("Error while uploading.", e);
throw e;
} catch (Exception e) {
log.error("Error while uploading.", e);
throw new RuntimeException(e);
}
}
I want to add a #RequestBody like this:
#RequestBody #RequestParam(value = "content", required = true) ????? content
this new content can contain anything.
How do I pass the content parameter correctly?

Declare a class like this
class PostBody{
Map<String, String> content;
}
use this PostBody as #RequestBody. You can pass any string key value pair to this structure. Something like this
{
"content":
{
"version" : "1.1",
"env" : "test"
}
}
or
{
"content":
{
"version" : "1.1",
"env" : "test",
"id" : "1.1"
}
}

Related

can I send Criteria in a spring feign client GetMapping request?

I'm trying to get userConfig from Organisation microservice, the app is getting quite large to I had too fetch UserConfig with userConfigCriteria for further use,
I wrote my feign service like this:
#AuthorizedFeignClient(name = "organization")
#Service
public interface UserConfigFeignService {
#RequestMapping(
value = "/api/feign-user-configs",
consumes = "application/json",
produces = "application/json",
method = RequestMethod.GET
)
ResponseEntity<List<UserConfigFein>> getUserConfig(UserConfigFeignCriteria criteria);
}
implemented like this:
public List<UserConfigFein> UserConfigByDepartment(UserConfigFeignCriteria criteria) {
try {
return userConfigFeignService.getUserConfig(criteria).getBody();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("feign error: couldn't get all userConfigs with criteria " + criteria);
}
}
in the Organisation microservice I created this end pt
#GetMapping("/feign-user-configs")
public ResponseEntity<List<UserConfigDTO>> getUserConfig(UserConfigCriteria criteria) {
log.debug("REST request to get a page of UserConfigs by criteria: {}", criteria);
//bla bla code that is not reached
}
an error is thrown at this point
throw new RuntimeException("feign error: couldn't get all userConfigs with criteria " + criteria);
[405 Method Not Allowed] during [GET] to [http://organization/api/feign-user-configs] [UserConfigFeignService#getUserConfigByDepartmentId(UserConfigFeignCriteria)]: [{
"title" : "Method Not Allowed",
"status" : 405,
"detail" : "Request method 'POST' not supported",
"path" : "/api/feign-user-configs",
"message" : "error.http.405"
}]
the end point in Organization is not being reached.
any ideas ?
I'm expecting
UserConfigFeignCriteria
to be sent to Organisation microservice
edit: here's UserConfigFeignCriteria
#ParameterObject
public class UserConfigFeignCriteria implements Serializable, Criteria {
private LongFilter id;
private StringFilter userId;
private StringFilter nickname;
private Boolean focusMode;
private LongFilter departmentId;
private Boolean distinct;
public UserConfigFeignCriteria(UserConfigFeignCriteria other) {
this.id = other.id == null ? null : other.id.copy();
this.userId = other.userId == null ? null : other.userId.copy();
this.nickname = other.nickname == null ? null : other.nickname.copy();
this.focusMode = other.focusMode;
this.distinct = other.distinct;
}
public UserConfigFeignCriteria() {}
#Override
public UserConfigFeignCriteria copy() {
return new UserConfigFeignCriteria(this);
}
//getters and setters
#Override
public boolean equals(Object o)
#Override
public int hashCode()
#Override
public String toString()
}
I solved the issue by changing the request method in interface UserConfigFeignService to POST like so
#AuthorizedFeignClient(name = "organization")
#Service
public interface UserConfigFeignService {
#RequestMapping(
value = "/api/feign-user-configs",
consumes = "application/json",
produces = "application/json",
method = RequestMethod.POST
)
ResponseEntity<List<UserConfigFein>> getUserConfig(UserConfigFeignCriteria criteria);
}
I have to make more research for why criteria can not be sent with a get method when using feign client, cause fetching data with POST methos is not a good solution.

Parsing nested json received from an api to objects in spring boot

I am creating a spring boot application which receives some JSON data from a 3rd party api. The JSON data has so many nested objects.I want to map them as Java objects. Below is the code I wrote for getting the api response.
public ResponseEntity<MovieSearchResultsDto> getMovies(String searchText, String countryCode) {
logger.info("GetMovies Service started");
String url = prepareUrl(searchText,countryCode);
HttpHeaders header = new HttpHeaders();
prepareHeader(header);
HttpEntity<String> requestEntity = new HttpEntity<String>(header);
try {
logger.info("Calling the API for movie info");
responseEntity = restClient.exchange(url,
HttpMethod.GET,
requestEntity,
MovieSearchResultsDto.class);
}catch (Exception e) {
logger.error("Exception occured while calling the API "+ e);
if(responseEntity.getStatusCodeValue() != 200) {
}
}
logger.info("GetMovies Service Ended");
return responseEntity;
}
And the JSON response looks like
{
"results": [
{
"id": ******,
"picture": "url",
"name": "Titanic",
"locations": [
{
"icon": "url",
"display_name": "Amazon Instant Video",
"name": "AmazonInstantVideoIVAGB",
"id": "***",
"url": "url"
}
],
"provider": "iva",
"weight": 0,
"external_ids": {
"iva_rating": null,
"imdb": {
"url": "url",
"id": "tt0046435"
},
"tmdb": {
"url": "url",
"id": "id"
},
"wiki_data": {
"url": "url",
"id": "id"
},
"iva": null,
"gracenote": null,
"rotten_tomatoes": null,
"facebook": null
}
}
] }
What I have done is , I created a class MovieSearchResultsDto and include a list as its data member with getters and setters.
private List<MoviesDto> results = new ArrayList<>();
And created MoviesDto class as below
public class MoviesDto {
private String id;
private String name;
private String picture;
#JsonInclude(value = Include.NON_EMPTY)
private List<MovieLocation> locations = new ArrayList<MovieLocation>();
#JsonInclude(value = Include.NON_EMPTY)
private List<ExternalIds> external_ids = new ArrayList<ExternalIds>();
public MoviesDto() {
}
//getters and setters
}
class MovieLocation{
private String icon;
private String id;
private String display_name;
private String name;
private String url;
public MovieLocation() {
}
//getters and setters
}
class ExternalIds{
private IdAndUrl imdb;
private IdAndUrl tmdb;
private IdAndUrl wiki_data;
public ExternalIds() {
}
//getters and setters
}
class IdAndUrl{
private String url;
private String id;
public IdAndUrl() {
}
//getters and setters
}
But it shows error while parsing.
Exception occured while calling the API org.springframework.web.client.RestClientException: Error while extracting response for type [class com.prebeesh1427.MovieNameServiceProvider.dto.MovieSearchResultsDto] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.ArrayList<com.prebeesh1427.MovieNameServiceProvider.dto.ExternalIds>` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<com.prebeesh1427.MovieNameServiceProvider.dto.ExternalIds>` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1054] (through reference chain: com.prebeesh1427.MovieNameServiceProvider.dto.MovieSearchResultsDto["results"]->java.util.ArrayList[0]->com.prebeesh1427.MovieNameServiceProvider.dto.MoviesDto["external_ids"])
I am a newbie to this area. Kindly help me not just only to resolve this issue but to understand the concept of these parsing techniques too.
Thanks in advance

Spring API #RequestParam Input conversion

I am creating a method for an API. In this method i have some parameters that are optional. those are filters for searching an event. when i try to run it and type the following url:
http://localhost:8181/api/events?id=gRDHzDh9TdiLDAZgrZc2wg==
i get this error message:
Failed to convert value of type 'java.lang.String' to required type 'java.util.UUID'; nested exception is java.lang.IllegalArgumentException: Invalid UUID string: gRDHzDh9TdiLDAZgrZc2wg==
So i understand that i insert a String in my url and expect a UUID in code, but how do i convert this? Below here is my code:
#RequestMapping(
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity getEvents(#RequestParam(value = "id", required = false) UUID eventId,
#RequestParam(value = "title", required = false) String title,
#RequestParam(value = "playtype", required = false) PlayType playType,
#RequestParam(value = "skilllevel", required = false) SkillLevel skillLevel,
#RequestParam(value = "sporttype", required = false) SportType sportType,
#RequestParam(value = "long", required = false) String _long,
#RequestParam(value = "lat", required = false) String lat) {
try {
List<Event> events = eventService.getEvents(eventId, title, playType, skillLevel, sportType, _long, lat);
if (events.size() == 0) {
return new ResponseEntity("No events found", HttpStatus.OK);
}
return new ResponseEntity(events, HttpStatus.OK);
} catch (Exception ex){
return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
So here are my 2 questions:
How do i convert the string to a valid UUID input in the RequestParam?
How do i convert the string to a valid enum in the RequestParam? (because with the enums i have the same error)
EDIT
my code is now like this:
#RequestMapping(
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity getEvents(#RequestParam(value = "id", required = false) String eventId,
#RequestParam(value = "title", required = false) String title,
#RequestParam(value = "playtype", required = false) String playType,
#RequestParam(value = "skilllevel", required = false) String skillLevel,
#RequestParam(value = "sporttype", required = false) String sportType,
#RequestParam(value = "long", required = false) String _long,
#RequestParam(value = "lat", required = false) String lat) {
UUID id = null;
PlayType playType1 = null;
SkillLevel skillLevel1 = null;
SportType sportType1 = null;
try {
if (eventId != null){
id = UUID.fromString(eventId);
}
if (playType != null){
playType1 = PlayType.valueOf(playType);
}
if (skillLevel != null){
skillLevel1 = SkillLevel.valueOf(skillLevel);
}
if (sportType != null){
sportType1 = SportType.valueOf(sportType);
}
List<Event> events = eventService.getEvents(id, title, playType1, skillLevel1, sportType1, _long, lat);
if (events.size() == 0) {
return new ResponseEntity("No events found", HttpStatus.OK);
}
return new ResponseEntity(events, HttpStatus.OK);
} catch (Exception ex){
return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
but i still get an error:
Invalid UUID string: gRDHzDh9TdiLDAZgrZc2wg==
How do I convert the string to a valid UUID input?
You need to use UUID.fromString() API, see here
How do i convert the string to a valid enum?
You need to use Enum.valueOf() API, see here

Expose enums with Spring Data REST

I'm using Spring Boot 1.5.3, Spring Data REST, HATEOAS.
I've a simple entity model:
#Entity
public class User extends AbstractEntity implements UserDetails {
private static final long serialVersionUID = 5745401123028683585L;
public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
#NotNull(message = "The name of the user cannot be blank")
#Column(nullable = false)
private String name;
/** CONTACT INFORMATION **/
private String landlinePhone;
private String mobilePhone;
#NotNull(message = "The username cannot be blank")
#Column(nullable = false, unique = true)
private String username;
#Email(message = "The email address is not valid")
private String email;
#JsonIgnore
private String password;
#Column(nullable = false)
private String timeZone = "Europe/Rome";
#JsonIgnore
private LocalDateTime lastPasswordResetDate;
#Column(nullable = false, columnDefinition = "BOOLEAN default true")
private boolean enabled = true;
#Type(type = "json")
#Column(columnDefinition = "json")
private Roles[] roles = new Roles[] {};
and my enum Roles is:
public enum Roles {
ROLE_ADMIN, ROLE_USER, ROLE_MANAGER, ROLE_TECH;
#JsonCreator
public static Roles create(String value) {
if (value == null) {
throw new IllegalArgumentException();
}
for (Roles v : values()) {
if (value.equals(v.toString())) {
return v;
}
}
throw new IllegalArgumentException();
}
}
I'm creating a client in Angular 4. Spring Data REST is great and expose repository easily return my model HATEOAS compliant:
{
"_embedded": {
"users": [
{
"name": "Administrator",
"username": "admin",
"roles": [
"Amministratore"
],
"activeWorkSession": "",
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/users/1"
},
"user": {
"href": "http://localhost:8080/api/v1/users/1{?projection}",
"templated": true
}
}
},
Like you can see I'm also translating via rest-messages.properties the value of my enums. Great!
My Angular page now needs the complete lists of roles (enums). I've some question:
understand the better way for the server to return the list of roles
how to return this list
My first attemp was to create a RepositoryRestController in order to take advantage of what Spring Data REST offers.
#RepositoryRestController
#RequestMapping(path = "/api/v1")
public class UserController {
#Autowired
private EntityLinks entityLinks;
#RequestMapping(method = RequestMethod.GET, path = "/users/roles", produces = "application/json")
public Resource<Roles> findRoles() {
Resource<Roles> resource = new Resource<>(Roles.ROLE_ADMIN);
return resource;
}
Unfortunately, for some reason, the call to this methods return a 404 error. I debugged and the resource is created correctly, so I guess the problem is somewhere in the JSON conversion.
how to return this list?
#RepositoryRestController
#RequestMapping("/roles")
public class RoleController {
#GetMapping
public ResponseEntity<?> getAllRoles() {
List<Resource<Roles>> content = new ArrayList<>();
content.addAll(Arrays.asList(
new Resource<>(Roles.ROLE1 /*, Optional Links */),
new Resource<>(Roles.ROLE2 /*, Optional Links */)));
return ResponseEntity.ok(new Resources<>(content /*, Optional Links */));
}
}
I was playing around with this and have found a couple of ways to do it.
Assume you have a front end form that wants to display a combo box containing priorities for a single Todo such as High, Medium, Low. The form needs to know the primary key or id which is the enum value in this instance and the value should be the readable formatted value the combo box should display.
If you wish to customize the json response in 1 place only such as a single endpoint then I found this useful. The secret sauce is using the value object PriorityValue to allow you to rename the json field through #Relation.
public enum Priority {
HIGH("High"),
NORMAL("Normal"),
LOW("Low");
private final String description;
Priority(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static List<Priority> orderedValues = new ArrayList<>();
static {
orderedValues.addAll(Arrays.asList(Priority.values()));
}
}
#RepositoryRestController
#RequestMapping(value="/")
public class PriorityController {
#Relation(collectionRelation = "priorities")
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
private class PriorityValue {
private String id;
private String value;
public PriorityValue(String id,
String value) {
this.id = id;
this.value = value;
}
}
#GetMapping(value = "/api/priorities", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity<Resources<PriorityValue>> getPriorities() {
List<PriorityValue> priorities = Priority.orderedValues.stream()
.map(p -> new PriorityValue(p.name(), p.getDescription()))
.collect(Collectors.toList());
Resources<PriorityValue> resources = new Resources<>(priorities);
resources.add(linkTo(methodOn(PriorityController.class).getPriorities()).withSelfRel());
return ResponseEntity.ok(resources);
}
}
Another approach is to use a custom JsonSerializer. The only issue using this is everywhere a Priority enum is serialized you will end up using this format which may not be what you want.
#JsonSerialize(using = PrioritySerializer.class)
#Relation(collectionRelation = "priorities")
public enum Priority {
HIGH("High"),
NORMAL("Normal"),
LOW("Low");
private final String description;
Priority(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static List<Priority> orderedValues = new ArrayList<>();
static {
orderedValues.addAll(Arrays.asList(Priority.values()));
}
}
#RepositoryRestController
#RequestMapping(value="/api")
public class PriorityController {
#GetMapping(value = "/priorities", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity<Resources<Priority>> getPriorities() {
Resources<Priority> resources = new Resources<>(Priority.orderedValues);
resources.add(linkTo(methodOn(PriorityController.class).getPriorities()).withSelfRel());
return ResponseEntity.ok(resources);
}
}
public class PrioritySerializer extends JsonSerializer<Priority> {
#Override
public void serialize(Priority priority,
JsonGenerator generator,
SerializerProvider serializerProvider)
throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("id");
generator.writeString(priority.name());
generator.writeFieldName("value");
generator.writeString(priority.getDescription());
generator.writeEndObject();
}
}
The final json response from http://localhost:8080/api/priorities
{
"_embedded": {
"priorities": [
{
"id": "HIGH",
"value": "High"
},
{
"id": "NORMAL",
"value": "Normal"
},
{
"id": "LOW",
"value": "Low"
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/priorities"
}
}
}

Get data as a JSON format in spring boot

I want to build a request endepoints using spring boot: I have to consume restful api and convert that into another rest endpoint.
I have a json Response on www.exampleapiurl.com/details
[{
"name": "age",
"value": "Child"
},
{
"name": "recommendable",
"value": true
},
{
"name": "supported",
"value": yes
},
]
[{
"name": "age",
"value": "Adult"
},
{
"name": "recommendable",
"value": true
},
{
"name": "supported",
"value": no
},
]
I want the response to be:
[{
"age": "Child"
},
{
"recommendable": true
},
{
"supported": "yes"
},
]
[{
"age": "Adult"
},
{
"recommendable": true
},
{
"supported": "no"
},
]
For this I have a attribute class with getter and setter:
Attributes.class
#JsonIgnoreProperties(ignoreUnknown = true)
public class Attributes {
private String age;
private boolean recommendable;
private String supported;
getter and setter for these:
}
This is my service.java class
#Service
public class CService {
private static RestTemplate restTemplate;
public String url;
#Autowired
public CService(String url) {
this.url = url;
}
public Attributes getAttributes() {
HttpHeaders headers= new HttpHeaders();
headers.add("Authorization", "some value");
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, Attributes.class);
return response.getBody();
}
}
And this is my controller.class
#Controller
public class CController {
private CService cService;
#Autowired
public CController(CService cService) {
this.cService = cService;
}
#RequestMapping(value="/example")
#ResponseBody
public Attributes getCAttributes() {
return cService.getAttributes(); }
}
The Authorization is successful but,
I am not getting any response for now
What you can do is create a model class to recive the response from example API
as follows.
#JsonIgnoreProperties(ignoreUnknown = true)
public class Details{
private String name;
private String value;
#JsonProperty("value")
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#JsonProperty("name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then change your invoking RestTemplate code as follows
Details[] details = null;
//Keeps the example API's data in array.
ResponseEntity<Details[]> response = restTemplate.exchange(url, HttpMethod.GET, request, Details[].class);
details = response.getBody();
//Next step is to process this array and send the response back to your client
List<Attributes> attributes = new ArrayList<Attributes>();
Attributes attr = null;
for(Details detail : details) {
attr = new Attributes();
//set the values here
}
//returns the attributes here
attributes.toArray(new Attributes[attributes.size()]);

Resources