Spring RESTful application - POST method request body mandatory attributes - spring-boot

I am building a RESTful app in Spring Boot and i want to make few attributes in my POST method's request body mandatory.
In swagger yaml, i mark them as required "true", but when i generate the classes using swagger editor, i dont see that impacting in any way, i.e i can't see even a #NotNull annotation or anything of that sort.
How do i mark them as mandatory in my java model class ? Is #NotNull the way to go?
If yes, should i do that in my request body class, or in the jpa document class or both ?
Thanks !

Yes, #NotNull is a way to go.
But also You need to use #Valid annotation.
check example:
#RequestMapping(value = "/appointments", method = RequestMethod.POST)
public String add(#Valid AppointmentForm form, BindingResult result) {
....
}
static class AppointmentForm {
#NotNull
private Date date;
}

Related

Spring Boot Validation - Request parameter

I'm having an issue with Spring Boot validation on a REST controller. I'm using validation 2.4.1.
I'm trying to validate elements of a list. Here's an example:
#Validated
#RestController
#RequestMapping("path")
class ControllerClass(
private val service: ServiceClass
) {
#GetMapping()
suspend fun controllerMethod(
request: ServerHttpRequest,
#RequestParam(required = false)
#NotEmpty
id: List<#Positive Int>? // Here's the issue.
): ResponseEntity<Map<String, Any>> {
// Content...
}
}
#NotEmpty works properly on the list object, so when the parameter is empty (example.com/path?id=), the client receives a validation error message. However, #Positive doesn't work on the Int objects inside the list. I did some research and some people say it works. I also check the #Positive interface and it supposed to work like that.
I also tried using #Valid everywhere, with the type use, parameter and method, but it doesn't work either.
Is there anything else I have to do? For example, I know that, to use validation on data classes in Kotlin, you have to use #get: (#get:Positive, for example) for validation to work.
Thank you!

How the request body of form is passed to spring mvc controller and how to map to pojo class instance?

How the request body of form is passed to the spring MVC controller and how to map to the POJO class instance?
I presume, you are building end point using POST. If yes, you can annotate method parameter with #RequestBody to a capture request body.
Basically, #RequestBody is used to bind the HTTP request body with a domain object in method parameter. Behind the scenes, these annotation uses HTTP Message converters to convert the body of HTTP request to domain objects.
#RequestMapping(value="/user/create", method=RequestMethod.POST)
public void createUser(#RequestBody User user){
// your logic goes here..
// Make sure that parameters in User POJO matches with HTTP Request Parameters.
}
I assume you are using POST API request for your use case. In the spring mvc controller class, we can make use of #RequestBody annotation followed by Pojo class.
Example:
#PostMapping
public ResponseEntity<ResponseDto> saveData(#RequestBody Data data) {
// Access to service layer
}
Let's say you're doing a POST request.
#PostMapping
public void saveSomeData(#RequestBody PojoClass pojoClass){
// whatever you wanna do
}
The #RequestBody annotation extracts the data your client application is sending on the body of the request. Also, you'd want to name the member variables on your pojo class similar to how you named it in on your request body.
Your POJO class can be something like:
public class PojoClass{
private String name;
private int age;
}
Now you can create getters and setters or use the Lombok annotation #Data on the class level to auto-generate the getters and setters.
Hope this was helpful:)

How to restrict JSON payload from containing additional fields with Spring?

I have a basic User DTO class...
public class User {
#JsonProperty("firstName")
private String firstName;
#JsonProperty("lastName")
private String lastName;
}
...and a basic request handler in a #RestController class:
#RequestMapping(path = "/users", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE })
public UserMessage createUser(#RequestBody User user){
return userService.createUser(user);
}
How can I restrict incoming JSON payloads to contain at most only the required keys?
i.e. accept this payload:
{
"firstName":"foo",
"lastName":"bar"
}
And throw a custom exception on this:
{
"firstName":"foo",
"lastName":"bar",
"type":"admin",
"asdf":"asdf"
}
I read about custom Converters, ArgumentResolvers, and I believe I could simply put an additional Map parameter in the handler and validate before service call, however I'd like to know the "best" way of handling this issue.
Regarding the User bean in your example it also already not possible, that potential other JSON fields than firstName and lastName could be mapped, simply because there are no fields in User which could hold the relevant data.
Should the User bean in your question be not complete, e.g. for simplicity reasons, and contain more fields, also then should everything be fine, as long as you did not configure your your ObjectMapper with com.fasterxml.jackson.databind.DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES => false or you use the annotation #JsonIgnoreProperties(ignoreUnknown = true) on your bean.
To sum it up: Jackson's default behavior is FAIL_ON_UNKNOWN_PROPERTIES (default: true)
For further information you can also consult the respective Deserialization docs.
Solved the issue, this thread helped
#JsonIgnoreProperties(ignoreUnknown=false) is not working in Spring 4.2.0 and upper version
mle, your answer wasn't right, since I was using the latest version of Spring Framework and the ObjectMapper's FAIL_ON_UNKNOWN_PROPERTIES is turned off by default. Additionally I was needed to set #JsonIgnoreProperties(ignoreUnknown = false) in my User DTO class (as the actual class' superclass had this set to true).
Tested it, runs like a charm, while custom errors can be handled in a #ExceptionHandler(HttpMessageNotReadableException.class) annotated handler.

How is a custom derived query POST added to spring data REST?

I have a Spring data REST project I am using to learn.
Right now I want to set up a query in this repository:
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Long>
namely, this guy:
#RestResource(path = "login", rel = "login")
User findByUsernameAndPassword(String username, String password);
A basic repository is easy to set up. So are custom GET requests like:
List<Item> findByType(#Param("type") String type);
or
#RestResource(path = "byMaxPrice")
#Query("SELECT i FROM Item i WHERE i.price <= :maxPrice")
List<Item> findItemsLessThan(#Param("maxPrice") double maxPrice);
But these are still GET requests. I would like to use a POST request. The method = RequestMapping.POST markup isn't accepted by #RestResource .. and I dont see any mention of different request types in the documentation. how do I do this?
You need to define a custom Controller to handle POST requests. If you just want to do POST for the default Repository methods, unfortunately, you still have to make a pass through Controller
#RepositoryRestController
public class MyController implements Serializable {
...
#RequestMapping(value = "/myConstroller/myPostMethod", method = RequestMethod.POST
, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody List<MyObjects> updateMyObjectList(
#RequestBody List<MyObjects> objects) {
// Call your repository method here, or a custom service, or whatever
}
See Spring MVC docs for further information, specifically section 5.2.1, which describes the default HTTP Method support for Repositories, and 16.4, which gives a custom Controller example.
From 5.2.1:
POST
Creates a new entity from the given request body.
Thus, POST is supported, but not to do what you are trying to do. If you want to "hide" URL parameters by using POST instead of GET, you need a custom Controller.

Spring #InitBinder on #RequestBody

I'm trying to use the #InitBind annotation to map only certain fields on the object in the request body.
I have a spring controller defined in this way:
#RequestMapping(value = "addAddress", method = RequestMethod.POST)
public Object addAddressToPerson(
HttpServletRequest request,
HttpServletResponse res,
#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "surname", required = false) String surname,
#RequestBody personDTO personJson,BindingResult result) {
The client request will be a a json representing a personDTO, but I don't want that field except the address to be mapped in the object for security reasons.
The input will be something like:
{ "address":"123 Street","........}
The personDTO contains many fields, and since spring map all of them directly in a DTO, that can be a problem.
I've seen that a solution is to use a Binder to declase the allowed or Disallowed field, but if I check the personDTO inside the controller, other fields are populate (for example if pass "id":"1234").
Any Hints?
The binder code is the following:
#InitBinder("orderJson")
protected void orderJsonBinder(WebDataBinder binder){
binder.setAllowedFields(new String[]{"address"});
}
Am I missing something?
Best Regards,
Luca.
But you are not binding request parameters to a model attribute bean, you are just asking spring to use an appropriate MessageConverter to convert the request body. As you say it is Json, you will use a MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter with Jackson 1.x). The Spring Reference Manual says for this converter :
[This is an] HttpMessageConverter implementation that can read and write JSON using Jackson's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).
#InitBinder can only configure binding of #ModelAttribute annotated parameters. It is useless here. If Jackson annotations are not enough, you will have to use a custom object mapper.
And I am surprised that you can use a BindingResult after a #RequestBody parameter, because the documentation says that it should follow a #ModelAttribute one.

Resources