How to validate a collection in spring-mvc POST webservice? - java

I have a simple spring webservice that offers a #PostMapping and takes a json array of elements.
I want spring to automatically validate each element in the list.
#RestController
public class PersonServlet {
#PostMapping
public void insertPersons(#RequestBody #Valid List<PersonDto> array) {
}
}
public class PersonDto {
#NotBlank
private String firstname;
#NotBlank
private String lastname;
}
The following POST request should fail with a validation error that firstname is missing:
[
{
"lastname": "john"
},
{
"firstname": "jane",
"lastname": "doe"
}
]
Result: the request is NOT rejected. Why?
Sidenote: if I just use PersonDto as parameter (not a list), and send a json post request with only one persons, the validation works and correctly rejects the request.
So in general the validation annotations seem to work, but just not when inside the collection!

Workaround: the following triggers the list validation:
public class PersonDtoList extends ArrayList<PersonDto> {
#Valid
public List<PersonDto> getList() {
return this;
}
}
public void insertPersons(#RequestBody #Valid PersonDtoList array) {
}

You should add another class outside the list, for example PostCommand:
public class PostCommand() {
#Valid
private List<PersonDTO> list;
}
and send it on the request:
#RestController
public class PersonServlet {
#PostMapping
public void insertPersons(#RequestBody #Valid PostCommand postCommand) {
}
}
and JSON will be:
{
"list": [
{
"lastname": "john"
},
{
"firstname": "jane",
"lastname": "doe"
}
]
}
And you will have an exception.

Related

#Valid annotation is not working for list of json request

I tried to validate the list of json request but it is not working.
Json request
[
{
"name": "AA",
"location": "Newyork"
},
{
"name": "BB",
"location": "Delhi"
}
]
Here is my controller class
Controller class
#RestController
#Validated
#RequestMapping("/v1.0")
public class Controller {
public ResponseEntity<List<Response>> ConversionResponse( #RequestBody List<#Valid Request> req) throws Throwable {
List<Response> response = new ArrayList<Response>();
for (request request : req) {
response = services.conversion(request, response);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
Here #Valid annotation doesnot validate the list
POJO class
public class Request{
#NotNull
private String name;
#NotNull(message="Location should not be null")
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
Please help me on this.
After Spring-boot 2.3.0, you need to manually add the
spring-boot-starter-validation to your project.
Use #RequestBody List<#Valid Req> reqs instead of #RequestBody #Valid List<Req> reqs.
Add #Validated on the Controller Class.
It will throw ConstraintViolationException, so you may want to map it into 400 Bad Request.
sources: Baeldung, Question 39348234

One #PreAuthorize("hasAuthority('ACCOUNT OWNER')" works, another very similar - throws SpelEvaluationException

I have problem with Spring Security and GraphQL.
I have two similar classes:
UserProfileQuery:
#Component
#AllArgsConstructor
class UserProfileQuery implements GraphQLQueryResolver {
private final UserProfileRepository userProfileRepository;
private final AddressRepository addressRepository;
#PreAuthorize("hasAuthority('ACCOUNT_OWNER')")
public List<UserProfile> getUserProfiles() {
return userProfileRepository.findAll();
}
#PreAuthorize("hasAuthority('ACCOUNT_OWNER')")
public Optional<UserProfile> getUserProfile(Long id) { return userProfileRepository.findById(id); }
#PreAuthorize("hasAuthority('ACCOUNT_OWNER', 'USER')")
public Set<Address> getAddresses(Long id) {
return Optional.of(addressRepository.findByUserId(id)).orElseThrow(() -> new NotFoundException(UserProfile.class));
}
}
and AddressQuery:
#Component
#AllArgsConstructor
class AddressQuery implements GraphQLQueryResolver {
private final AddressRepository addressRepository;
#PreAuthorize("hasAuthority('ACCOUNT_OWNER')")
public List<Address> getAddresses() {
return addressRepository.findAll();
}
#PreAuthorize("hasAnyAuthority('ACCOUNT_OWNER', 'USER')")
public Address getAddress(Long id) {
return addressRepository.findById(id).orElseThrow(() -> new NotFoundException(Address.class));
}
}
Both entities, UserProfile and Address have their own distinct graphqls files. Making it short, I'll just post important fragments of those files:
userprofile.graphqls:
extend type Query {
userProfiles: [UserProfile]
addresses(id: ID): [Address]
userProfile(id: ID): UserProfile
}
and address.graphqls:
extend type Query {
addresses: [Address]
address(id: ID): Address
}
Now, when I am querying UserProfiles:
query {
userProfiles {
id
fullName
}
}
I get a regular answer (just sample data, don't mind it):
{
"data": {
"userProfiles": [
{
"id": "1",
"fullName": "adgadgd"
},
{
"id": "2",
"fullName": "adgadgd"
}
]
}
}
but making similar query on Addresses:
query {
addresses {
id
city
}
}
I get:
{
"data": {
"addresses": null
},
"errors": [
{
"extensions": null,
"message": null,
"locations": [],
"errorType": "DataFetchingException",
"path": null
}
]
}
and in the console I get an error:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method hasAuthority(java.lang.String,java.lang.String) cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
What am I missing? Both Queries are similar, and only one works...
Finally, it seems that GraphQL doesnt like same names in Query/Mutation methods. In AddressQuery I have changed method name from
public List<Address> getAddresses()
to
public List<Address> getAllAddresses() {
and propagated this change to address.graphqls:
extend type Query {
allAddresses: [Address]
address(id: ID): Address
}
Now the query to get all addresses in AddressQuery works like a charm.

Map Complex Json to Pojo Class

I am sending the following request (using Spring Boot)
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
response is the json object(i have ommitted lot of fields in json object)
{
"customer": {
"id": 100,
"ci": {
"lDb": "11",
"localId": "1"
},
"cusdata": {},
"rating": {
"id": 3140,
"dateTime": "2019-09-21 06:45:41.10",
"rawData": {
"seg": "XYZ",
"seg2": "XYZ",
"et": "XYZ_CORP",
"CountryCodes": [
"IN"
],
"doBusiness": "2017-09-20"
],
...
....
...
...
"status": "SUCCESS"
}
I need to map the below fields to a Pojo Class
1.localId
2.seg
3.seg2
4.status
How can i create the PojoClass such that those fields are mapped automatically
So basically how will my PojoClass should look like?
ResponseEntity<PojoClass> response = restTemplate.exchange(url, HttpMethod.GET, request, PojoClass.class);
I suggest that you use sites like http://www.jsonschema2pojo.org/. There, you can select many options on the right panel and adjust POJO you want to get from JSON schema.
Your PojoClass has to follow the structure of the JSON that your are receiving and have the fields that your are interested (or all of them).
For the first level class:
public class PojoClass {
private Customer customer;
private String status;
...
}
Then, create a Customer class for the customer fields and create more classes for the rest of the fields:
public class Customer {
public String id;
public CI ci;
public CustData custData;
...
}
Create a custom class PojoClass
public class PojoClass {
private Integer id;
private Object ci;
private Object cusdata;
private Object rating;
private Object status;
}
ResponseEntity<PojoClass> responseEntity = restTemplate.exchange(url,HttpMethod.GET,request,new ParameterizedTypeReference<PojoClass>(){
});

java jpa json standard

First, thank you very much for reading this question.
I have a JPA project and everything works fine, the json that i get with the controller is of this form:
{"id": 1, "name": "Canada"},{"id": 2, "name": "USA"}
All its fine but i would like to get a json with the Jsend standard, it something like this:
{
status : "success",
data : {
"country" : [
{"id": 1, "name": "Canada"},
{"id": 2, "name": "USA"}
]
}
}
{
"status" : "fail",
"data" : { "title" : "A title is required" }
}
{
"status" : "error",
"message" : "Unable to communicate with database"
}
As you can see i want to have a status that says success, fail or error:
But i dont know how to do it. This is my DTO, DAO and Controller
#Entity
public class Country implements Serializable {
private static final long serialVersionUID = -7256468460105939L;
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name;
//Constructor, get and set
DAO
#Repository
#Transactional
public class CountryRepository {
#PersistenceContext
EntityManager entityManager;
public CountryDTO findById(int id) {
return entityManager.find(CountryDTO.class, id);
}
}
Controller
#RestController
public class CountryController {
#Autowired
CountryDTO repository;
#RequestMapping(value="api/country/{id}", method=RequestMethod.GET)
public #ResponseBody CountryDTO getByID(#PathVariable("id") int id){
return repository.findById(id);
}
}
Again thank you for your time.
Its very good question from my point of view. So I can give list of action items to achieve this.
You should aware of #ControllerAdvice annotation which is available in Spring.
By utilizing that you can play with your response object.
Then You should create your own Object which is similar to JSend. In my case, I have created JSendMessage class
public class JSendMessage {
String status;
String message;
Object data;
// Add getter and setter
}
Now you should map above class with your #ControllerAdvice return your required object.
So whenever there is a exception you can create and send your own custom exception message.
There will be lot of reference for this. Just look for #ControllerAdvice

REST with nested JSON

I have a JSON Object
{
"firstname": "foo",
"account":
{
"city": "bar"
}
}
which I want serialize in a REST backend:
#POST
public Response create(Employee e)
Classes:
public class Employee {
public String firstname;
private Address address;
}
public class Address {
public String city;
}
Unforutanetly, I always receive a 400 status code. But if I do
public Response create(Object o)
everything works fine. Any ideas?
Your JSON does not correspond (map) to your POJO types. In the JSON, you have account, but in your Java type you have address.

Categories