Request body Object Not being Validated - spring

I have one REST API which is using a POST Call to create a record, I'm expecting certain Object to be passed in post call, if anything is missing i have to reject straight away from their only,
#RequestMapping(value="/saveEssentialDetails",produces={"application/json"},method=RequestMethod.POST)
ResponseEntity<?> saveEssentialDetails(#ApiParam(value="Body Parameters")#RequestBody #Validated EssentialDetails essentialDetails, BindingResult bindingResult)throws Exception;
and the Essential Model class is as follow
#Data
#NoArgsConstructor
#Document(collection="essentialDetails")
public class EssentialDetails {
#NotNull
Integer dpId;
#Id
#NotEmpty
String tpId;
#NotEmpty
List<FamousFor> famousFor;
#NotEmpty
List<OpenHours> openHours;
#NotEmpty
Pictures uploadedImages;
#NotEmpty
List<FloorDescription> floorDescriptions;
#NotEmpty
List<Outlets> mallOutlets;
}
But while making a Post Call with Missing attributes i'm allowed to make an entry in MongoDB, which i don't want to persist as it's not a proper request,#Validation is not working for me, i'm using spring boot 2.0.6 with MongoDb 4.0.4,
any help would be highly appreciated. Thanks well in advance

#Validated can be used to validate a object with a custom validation object. Example usage:
#RequestMapping(value = "/")
public String request(#Validated(Account.ValidationStepOne.class) Account account)
Instead of using #Validated use #Valid which does check for the validation annotations that you are using in your entity.

Related

OpenApi add example for request body

Am working with Spring boot and I am using springdoc-openapi-ui to generate spec files using swagger editor
The issue Is, Am trying to avoid creating model classes just to use them in the request body to show them with swagger UI.
For example :
#RequestMapping(value = "/update/project/{id}", method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> projectUpdate(#RequestBody ObjectNode json, #PathVariable int id)
{ ... }
If I use it like this, the example will be empty on the Swagger UI page.
So as a solution, I have to do it like the following
public class CustomerCreateRequest {
#JsonProperty
private String ProjectId;
#JsonProperty
private String ProjectName;
#JsonProperty
private String ProjectDescription;
#JsonProperty
private String CustomerId;
#JsonProperty
private String CustomerName;
#JsonProperty
private String CustomerDescription;
#JsonProperty
private String BucketId;
#JsonProperty
private String API_KEY;
#JsonProperty
private String Name;
#JsonProperty
private String RedmineId;
And then I can use the model class I just created like the following.
#PostMapping(value = "/createUser")
public ResponseEntity createCustomer(#RequestBody CustomerCreateRequest requestBody)
{ ... }
Question
Is it ok to do a model class just for this purpose?
Is there a way to add an example so the UI team will have an idea of how to use it.
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Any Answer, Suggestion, Links are appreciated, the swagger docs was not helpful in my case.
my two cents:
Is it ok to do a model class just for this purpose?
Yes, you should use a model class for your #RequestBody becouse every endpoint must have a contract to communicate the payload necessary to be consumed.
It's a good practice add the annotations like
#Parameter(description="Some description", example="{\"foo\":\"bar\"}")
#RequestBody CustomerCreateRequest requestBody
Is there a way to add an example so the UI team will have an idea of how to use it.
No, Swagger will map a POJO class with decorators such as #Schema and others. ObjectNode has not a valid representation for the use case
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Well, in my experience use tools as Swagger have more benefits than cons. It's necessary take care about the constraints related? I think so

Spring MVC: how to avoid duplicating validation at controller and entity

Let's say I have a "Person" #Entity managed via JPA, which has a series of validations applied at entity-level (#NotBlank, #NotNull etc).
#Entity
public class Person {
#NotBlank
private String name;
#Email
private String email;
...
}
For various reasons, we shouldn't directly use an entity as the controller method argument, but rather create a custom "form" (taking the example from https://spring.io/guides/gs/validating-form-input/)
#PostMapping("/person/save")
public String savePerson(#Valid PersonForm personForm, BindingResult bindingResult) {
// map fields to Person entity individually and save
...
}
But now, to make use of Spring's built-in form validation / BindingResult, it appears I have to duplicate all my validation logic on both the Person and PersonForm classes. I don't just want to define them on PersonForm, because there might be other routes in the application to update a Person.
Ideally there would be some way that Spring could lift up the validation constraints on the #Entity and apply them to the form (e.g. if the properties had the same name).
Am I missing something here with validation?

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

Spring data #ReadOnlyProperty causing unexpected behavior

I have a Model attribute that needs to set #ReadOnlyProperty so that it won't persist after first inserting the line.
Assume my model like below
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
#JsonIgnore
private Long id;
#Column(unique = true, nullable = false)
#ReadOnlyProperty
private String openId;
}
then I have a UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
}
then I provide 2 Restful API for POST and PUT.
The create user operation code is as simple as below:
user.setOpenId(1);
userRepository.save(user)
The update user operation is almost the same:
user.setOpenId(2);
user = userRepository.save(user);
I'm surprised that the user's openId attribute will be changed, after POST and then PUT, the returned user object will have the changed value.(user.getOpenId() == 2)
It looks like #ReadOnlyProperty not working, I'm using the RELEASE version of spring-boot-starter-data-jpa. Can someone help explain?
It seems that #ReadOnlyProperty doesn't work. The following bug report is open for years:
Properties with #ReadOnlyProperty annotation are being nullified in PATCH requests
If you want to deny modifying the property via Spring Data Rest endpoints, use the #JsonProperty(access = Access.READ_ONLY) annotation. It affects the JSON deserialization, so the annotated property never reaches Spring Data Rest.
If you also need to deny the writing of the property via Spring Data JPA, you can use the following JPA annotation: #Column(updatable=false) It denies the override on the underlaying JPA level, instead of Spring Data JPA level.

Spring: How do I construct this command object through a GET HTTP request?

I'm using Spring 3.1.0.RELEASE with Hibernate 4.0.1.Final. I want to invoke a search method in a controller that takes as input a search bean (the Event bean below) ...
#RequestMapping(value = "/search_results.jsp")
public ModelAndView processSearch(final HttpServletRequest request, final Event searchBean, final BindingResult result) {
...
}
The event bean contains the following field ...
#Entity
#Table(name = "EVENTS")
public class Event implements Comparable {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="EVENT_FEED_ID")
private EventFeed eventFeed;
...
}
in which the EventFeed object contains the following fields ...
#Entity
#Table(name = "EVENT_FEEDS")
public class EventFeed {
#Id
#Column(name = "ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#NotEmpty
#Column(name = "TITLE")
private String title;
...
}
How do I construct a URL such that the search bean's Event.getEventFeed().getId() field is populated?
I realize I could submit a GET request with a parameter like "eventFeedId=2" and populate everything manually, but since other pages are submitting requests that populate the command object, I'd like to continue to use the same logic.
It would be
/search_results.jsp?event.eventFeed.id=...&event.eventFeed.title=...
event is a default model attribute name as defined in #ModelAttribute, other binding rules are described in 5.4.1 Setting and getting basic and nested properties.
Note, however, that this approach can cause problems if you'll associate these bean with Hibernate session later. For example, if you want to attach new Event to the existing EventFeed by calling merge() it would also override the title property. Thus, in such a case it would be better to avoid overuse of data binding and pass primitives as parameters instead.

Resources