How to validate #QuerydslPredicate in spring #RestController? - java

I'm using spring-data-rest to expose a database content via a spring servlet. Using #QuerydslPredicate for being able to send a filter via HTTP GET, like:
localhost:8080/persons?firstname=john&lastname=doe
Question: is it possible to tell the Predicate validation rules? Eg that the specific fields may not be null, eg lastname?
#RestController
public class PersonServlet {
#GetMapping("/persons")
public Iterable<Person> getPersons(
#QuerydslPredicate(root = Person.class) com.querydsl.core.types.Predicate predicate,
Pageable pageable) {
return dao.findAll(predicate, pageable);
}
#Entity
public class Person {
private String firstname;
private String lastname;
private String age;
//many more fields
}
}

There seems to be no way validating eg QPerson.lastname != null.
So I used the following approach adding #Valid Person as get parameter and adding validation constraints like #NotNull.
#RestController
public class PersonServlet {
#GetMapping("/persons")
public Iterable<Person> getPersons(
#QuerydslPredicate(root = Person.class) com.querydsl.core.types.Predicate predicate,
Pageable pageable,
#Valid Person p) {
return dao.findAll(predicate, pageable);
}
#Entity
public class Person {
private String firstname;
#NotNull
private String lastname;
private String age;
//many more fields
}
}

Related

How to combine each element of a Flux with another Flux that needs an element's attribute

I have a question about Spring WebFlux and Reactor.
I am trying to code a simple scenario where in a GET endpoint i return a Flux
of DTOs representing entities, and each of these entities has a collection of
other DTOs representing another entity. Here follow the details.
I have two entities, Person and Song, defined as follows:
#Data
public class Person {
#Id
private Long id;
private String firstName;
private String lastName;
}
#Data
public class Song {
#Id
private Long id;
private String title;
private Long authorId;
}
the entities are represented by the following DTOs:
#Data
public class SongDTO {
private Long id;
private String title;
public static SongDTO from(Song s) {
// converts Song to its dto
}
}
#Data
public class PersonDTO {
private Long id;
private String firstName;
private String lastName;
private List<SongDTO> songs = new ArrayList<>();
public static PersonDTO from(Person p, List<Song> songs) {
// converts person to its dto, invokes SongDTO.from on each song
// and adds the result to personDTO.songs
}
}
My services (not shown here for the sake of brevity) do return Mono and Flux.
Then I have the the following RestController:
#RestController
#RequestMapping("/people")
public class PersonController {
#Autowired PersonService people;
#Autowired SongService songs;
#GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<PersonDTO> findAllPeople() {
return people.findAll()
.map(person -> PersonDTO.from(person, /* HERE */ new ArrayList<>()));
// TODO: find the songs of each author reactively and put the results in personDTO.songs
}
}
Now, my problem is: how do I
call songs.findByAuthorId(person.getId())
convert the returned Flux to a List of SongDTO
set the list in PersonDTO
in a reactive way?
I tried to look into Reactor's documentation without success, searched other StackOverflow questions and
the internet at large, but couldn't find anything, most probably because I am
not really sure about how to phrase my search.
Can someone please provide hints?
Thank You
You can use flatMap + map:
people.findAll()
.flatMap(person -> songs.findByAuthorId(person.getId())
.collectList()
.map(songList -> PersonDTO.from(person, songList)));

Java annotation for request fields

I have an API where I want to validate some String fields using a custom #Annotation. Validation should happen when the user sends the data. If validation fails, I want to throw an exception. Validation will be complex, as I want to check for SQL & HTML injections.
I got the following Request Object:
public class UserUpdateRequest extends BasicDataObject {
private static final long serialVersionUID = 1295104288600535600L;
// I would like to validate the firstName value upon receiving data
#CustomValidator
private String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
This is my BasicDataObject :
#XmlRootElement
public abstract class BasicDataObject implements Serializable {
/**
* The generated serialVersionUID.
*/
private static final long serialVersionUID = 2876242398874879466L;
// some more stuff
}
How would I achieve this? I know that I can create custom annotation interfaces like this:
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(value = RetentionPolicy.RUNTIME)
public #interface CustomValidator {
String value();
}
But I do not know where to actually put my validation logic so that it is called on every field that I annotate with #CustomValidator. I do not use Spring. Any help is appreciated!
EDIT:
This is my web adapter using javax where I get the UserUpdateRequest:
#Path("/users")
#Produces("application/json")
#Consumes("application/json")
#GlobalSecurityResponse
public class UserAdapter {
#PUT
#JWTSecured(tokenType = UserTokenTypeEnum.AUTH, permissions = { UserEndpointPermissionsEnum.ACTIVE })
public Response updateUser(#Context SecurityContext securityContext, final UserUpdateRequest userUpdateRequest) {
Principal principal = securityContext.getUserPrincipal();
long userId = userBoundary.getPrincipalUserId(principal);
UserLoginResponse userLoginResponse = userBoundary.updateUser(userId, userUpdateRequest);
return Response.ok(userLoginResponse).build();
}
}
You created interface but does not created validator class which will be used by that interface.
Your validation logic will go inside isValid method of validator class.
You can find implementation here

#JsonCreator not working for #RequestParams in Spring MVC

#JsonCreator not deserialising #RequestParam of type enum
I am working on a Spring application where the controller is receiving list of request params that Spring is binding to a wrapper object. One of the params is of type enum where I am receiving it by some property name.
Endpoint example: http://localhost:8080/searchCustomers?lastName=Smith&country=Netherlands
#RequestMapping(value = "/search/customers", method = RequestMethod.GET)
public CustomerList searchCustomers(#Valid CustomerSearchCriteria searchCriteria)
public class CustomerSearchCriteria {
private String lastName;
private Country country;
}
public enum Country {
GB("United Kingdom"),
NL("Netherlands")
private String countryName;
Country(String countryName) {
countryName = countryName;
}
#JsonCreator
public static Country fromCountryName(String countryName) {
for(Country country : Country.values()) {
if(country.getCountryName().equalsIgnoreCase(countryName)) {
return country;
}
}
return null;
}
#JsonValue
public String toCountryName() {
return countryName;
}
}
I am expecting Spring to bind enum Country.Netherlands to CustomerSearchCriteria.country but its not doing it so. I tried similar annotations with #RequestBody and that works fine, so I am guessing he Spring binding is ignoring #JsonCreator.
Any helpful tips would be appreciated.
Here is the code that is behind #Mithat Konuk comment.
Put in your controller something like:
import java.beans.PropertyEditorSupport;
#RestController
public class CountryController {
// your controller methods
// ...
public class CountryConverter extends PropertyEditorSupport {
public void setAsText(final String text) throws IllegalArgumentException {
setValue(Country.fromCountryName(text));
}
}
#InitBinder
public void initBinder(final WebDataBinder webdataBinder) {
webdataBinder.registerCustomEditor(Country.class, new CountryConverter());
}
}
More information ca be found here: https://www.devglan.com/spring-boot/enums-as-request-parameters-in-spring-boot-rest.

Change JSON property name on derived classes using Jackson library

I am using Spring to develop new REST API, I have BaseResponse class which acts as base responses for all response. this class contains attribute String requestUuid; at some cases this requestUuid must be serialized with attribute name requestUuid , on other cases it must be seriliazed as request_uuid, i know i can use #JsonProperty as a field level annotation, but it will affect all responses. is there is any way to override attribute name specifically for each one of the derived classes.
You can use the #JsonProperty on the method level instead. That way, you can override the field's getter method in the subclass and annotate that.
For example:
class BaseResponse {
private String requestUuid;
public getRequestUuid() {
return requestUuid;
}
}
class OtherResponse extends BaseResponse {
#Override
#JsonProperty("request_uuid")
public getRequestUuid() {
return super.getRequestUuid();
}
}
You can send the field twice with different key names.
#JsonAnyGetter
public Map<String, Object> otherFields() {
Map<String, Object> otherFields = new HashMap<>();
otherFields.put("requestUuid", this.requestUuid);
otherFields.put("request_uuid", this.requestUuid);
return otherFields;
}
Also, ignore your actual field:
#JsonIgnore
private String requestUuid;
Expanding on #JoshA response, another alternative is to define a constructor and annotate it. This leads to a more concise code by avoiding the need to override the getter methods in derived classes.
class BaseResponse {
private String firstName;
private String lastName;
public BaseResponse(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public getFirstName() {
return firstName;
}
public getLastName() {
return lastName;
}
}
class OtherResponse extends BaseResponse {
public OtherResponse(#JsonProperty("given_name") String firstName, #JsonProperty("family_name") String lastName) {
super(firstName, lastName);
}
}
NO, its not possible, what is possible you can make new class for different type of requests.

Spring validation for list of nested class

I have implemented my validation for list of custom class as mention in this post. For reference here my code looks like
class TopDtoForm {
#NotEmpty
private String topVar;
private List<DownDto> downVarList;
//getter and setter
}
class DownDto {
private Long id;
private String name;
//getter and setter
}
#Component
public class TopDtoFormValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return TopDtoForm.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
TopDtoForm topDtoForm = (TopDtoForm) target;
for(int index=0; index<topDtoForm.getDownVarList().size(); index++) {
DownDto downDto = topDtoForm.getDownVarList().get(index);
if(downDto.getName().isEmpty()) {
errors.rejectValue("downVarList[" + index + "].name", "name.empty");
}
}
}
}
So even I send empty name binding result has 0 error. I tested with topVar and it is working fine. My question is do I have to do any other configuration to say use this validator?
Thanks
In Spring MVC just annotate in TopDtoForm your list with #Valid and add #NotEmpty to DownDto. Spring will validate it just fine:
class TopDtoForm {
#NotEmpty
private String topVar;
#Valid
private List<DownDto> downVarList;
//getter and setter
}
class DownDto {
private Long id;
#NotEmpty
private String name;
//getter and setter
}
Then in RequestMapping just:
#RequestMapping(value = "/submitForm.htm", method = RequestMethod.POST) public #ResponseBody String saveForm(#Valid #ModelAttribute("topDtoForm") TopDtoForm topDtoForm, BindingResult result) {}
Also consider switching from #NotEmpty to #NotBlank as is also checks for white characters (space, tabs etc.)

Categories