I have one Java REST API which is used by 2 different consumers. By default REST principles my API should define the names of request headers. But now I have not common situation. Consumers use different security layer which provides different headers which means same parameter in both ways.
Example method: (not real code)
For 1st consumer:
#PostMapping("/number")
Integer getNumber(#RequestHeader("no") String number, #RequestBody User user) {
/*...*/
}
For 2nd consumer:
#PostMapping("/number")
Integer getNumber(#RequestHeader("number") String number, #RequestBody User user) {
/*...*/
}
I have up to 10 methods in one controller and they should be with same name and logic, but different header. The request path prefix could be different.
Question:
How to simplify REST controller and don't create 2 different controllers with same methods and same logic?
What I tried:
I tried several examples to create one controller with 2 different interfaces with same methods, but different mapping.
Example:
Controller class
#RestController
#RequestMapping(path ="/application")
#Api(tags = {"application"})
public class ApplicationController implements AppMapping1, AppMapping2 {
#Override
public Integer getNumber(String number, User user) {
/*...*/
}
}
First interface
interface AppMapping1 {
#PostMapping("/num")
Integer getNumber(#RequestHeader("num") String number, #RequestBody User user);
}
Second interface
interface AppMapping2 {
#PostMapping("/number")
Integer getNumber(#RequestHeader("number") String number, #RequestBody User user);
}
Result:
Controller maps only with the first interface. So http://.../application/num works fine, but http://.../application/number - gets 404 error code. That means Java Spring-Boot doesn't have such functionality. Need some more ideas.
Project developed with Java 8; spring-boot:2.1.1.RELEASE; gradle
According to this , If we're not sure which headers will be present, or we need more of
them than we want in our method's signature, we can use the
#RequestHeader annotation without a specific name.
You have a few choices for variable type: a Map, a MultiValueMap or an HttpHeaders object.
Sample
#PostMapping("/number")
public Integer getNumber(#RequestHeader Map<String, String> headers) {
if (Optional.ofNullable(headers.get("no")).isPresent()){
//...
}
else if (Optional.ofNullable(headers.get("number")).isPresent())
{
//...
}
}
It is not maintenance friendly to repeat the same block of code twice or more times just to receive the same input with different names (number and no). Instead, it is advisable to read all the headers and traverse through it to fetch input using different names.
Sample Code
#PostMapping("/number")
public Integer getNumber(#RequestHeader Map<String, String> headers) {
String number = headers.containsKey("number") ? headers.get("number") : headers.get("no");
if(Objects.isNull(number)) {
throw new RuntimeException("Number input not received from header!");
}
// relevant processing
}
I found this answer on https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/spring-mvc-request-header.html
Avoid ambiguity by using #RequestMapping(headers = ....)
We can fix the ambiguity similar to #RequestParam where we used
'params' . In case of #RequestHeader we can define related headers in
#RequestMapping annotation.
#Controller
#RequestMapping("trades")
public class TradesController {
#RequestMapping(headers = "User-Agent")
public String handleAllTradesRequests (#RequestHeader("User-Agent") String userAgent,
Model model) {
model.addAttribute("msg", "all trades requests, User-Agent header : "
+ userAgent);
return "my-page";
}
#RequestMapping(headers = "From")
public String handleRequestByFromHeader (#RequestHeader("From") String from,
Model model) {
model.addAttribute("msg", "trade request by From header : " + from);
return "my-page";
}
You could remove the #RequestHeader annotation and consider doing the following:
#PostMapping("/number")
Integer getNumber(HttpServletRequest request, #RequestBody User user) {
String number = request.getHeader("num");
if(number == null){
number = request.getHeader("number");
}
/*...*/
}
If you want a cleaner approach, consider creating a util class that takes the HttpServletRequest object and returns the desired header value.
The best way is to add the HttpServletRequest as an argument of your single controller and do some logic with the header map provided by the HttpServletRequest object.
If you want to see a full example take a look here. I have implemented I single controller that wraps all my logic accordingly to headers/methods and so on. You can customize the logic as you want with the HttpServletRequest.
Related
Hello I have question about #ModelAttribute annotation. As i understand, we use #ModelAttribute in method arguments to get data from the model. But it's quite hard to understand clearly when and how its used.
(Code samples are from Spring in Action 5 book)
Why in this case in the code below in public String processOrder() method we do not use #ModelAttribute annotation on #Valid Order order
#Controller
#RequestMapping("/orders")
#SessionAttributes("order")
public class OrderController {
private OrderRepository orderRepo;
public OrderController(OrderRepository orderRepo) {
this.orderRepo = orderRepo;
}
#GetMapping("/current")
public String orderForm(#AuthenticationPrincipal User user,
#ModelAttribute Order order) {
if (order.getDeliveryName() == null) {
order.setDeliveryName(user.getFullname());
}
//following conditions
return "orderForm";
}
#PostMapping
public String processOrder(#Valid Order order, Errors errors, // <<< Here
SessionStatus sessionStatus,
#AuthenticationPrincipal User user) {
if (errors.hasErrors()) {
return "orderForm";
}
order.setUser(user);
orderRepo.save(order);
sessionStatus.setComplete();
return "redirect:/";
}
}
but in this case, DesignTacoController class, #ModelAttribute on a method processDesign() is used on #Valid Taco taco:
#Slf4j
#Controller
#RequestMapping("/design")
public class DesignTacoController {
#PostMapping
public String processDesign(#Valid #ModelAttribute("design") Taco design, // <<< Here
Errors errors, Model model) {
if (errors.hasErrors()) {
return "design";
}
// Save the taco design...
// We'll do this in chapter 3
log.info("Processing design: " + design);
return "redirect:/orders/current";
}
And then in the next chapter author removes #ModelAttribute from processDesign() method from the same DesignTacoController class.
#Controller
#RequestMapping("/design")
#SessionAttributes("order")
#Slf4j
public class DesignTacoController {
#ModelAttribute(name = "order")
public Order order() {
return new Order();
}
#ModelAttribute(name = "design")
public Taco design() {
return new Taco();
}
#PostMapping
public String processDesign(
#Valid Taco taco, Errors errors, // <<< Here
#ModelAttribute Order order) {
log.info(" --- Saving taco");
if (errors.hasErrors()) {
return "design";
}
Taco saved = tacoRepo.save(taco);
order.addDesign(saved);
return "redirect:/orders/current";
}
And in this code snippet(from the code above):
#PostMapping
public String processDesign(
#Valid Taco taco, Errors errors, // <<< Here
#ModelAttribute Order order) {
....
}
quote from book: "The Order parameter is annotated with #ModelAttribute to indicate that its
value should come from the model and that Spring MVC shouldn’t attempt to bind
request parameters to it."
This I don't understand what author meant here, because in all tutorials it is said that when #ModelAttribute is used as a method arguments,it binds request parameters to it. Binds the form data with a POJO bean, model attribute is populated with data from a form submitted.
The documentation is pretty clear on this:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
#ModelAttribute
For access to an existing attribute in the model (instantiated if not
present) with data binding and validation applied. See #ModelAttribute
as well as Model and DataBinder.
Note that use of #ModelAttribute is optional (for example, to set its
attributes). See “Any other argument” at the end of this table.
.
Any other argument
If a method argument is not matched to any of the earlier values in
this table and it is a simple type (as determined by
BeanUtils#isSimpleProperty, it is a resolved as a #RequestParam.
Otherwise, it is resolved as a #ModelAttribute.
So essentially it is optional. You may wish to use just to make it explicit that that is how the argument is resolved or you may need to use if binding should not happen (by specifying binding = false) See futher: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html. It is normally my preference to specify it regardless.
This wasn't clear to me either.
Here we need specify the name if the model attribute.
Because in our view we assume it is named "design" and not "taco".
#PostMapping
public String processDesign(#Valid #ModelAttribute("design") Taco design, Errors errors) {
If we rename the Taco class to Design ...
We don't need to specify the name if the model attribute.
It will be deduced from the simple name of the class.
com.example.Design -> "design"
#PostMapping
public String processDesign(#Valid Design design, Errors errors) {
See the javadoc for ModelAttribute:
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name: e.g. "orderAddress" for class
"mypackage.OrderAddress", or "orderAddressList" for
"List".
Okay so I am using a payment service called Thrive cart, I am doing this for a membership website I'm creating. When the user has paid I want them to be redirected to a URL where I can use that data to update the current users information.
The data that get's sent in the params is insane:
http://localhost:5000/user/welcome?thrivecart%5Baccount_id%5D=3196&thrivecart%5Baccount_name%5D=testacount&thrivecart%5Bcustomer%5D%5Bemail%5D=testname8%40gmail.com&thrivecart%5Bcustomer%5D%5Baddress%5D%5Bcountry%5D=GB&thrivecart%5Bcustomer%5D%5Baddress%5D%5Bzip%5D=pe303wu&thrivecart%5Border%5D%5B0%5D%5Bt%5D=product&thrivecart%5Border%5D%5B0%5D%5Bid%5D=6&thrivecart%5Border%5D%5B0%5D%5Bn%5D=Monthly+membership&thrivecart%5Border%5D%5B0%5D%5Bp%5D=799&thrivecart%5Border%5D%5B0%5D%5Bq%5D=1&thrivecart%5Border%5D%5B0%5D%5Bpo%5D=60120&thrivecart%5Border%5D%5B1%5D%5Bt%5D=product&thrivecart%5Border%5D%5B1%5D%5Bid%5D=6&thrivecart%5Border%5D%5B1%5D%5Bn%5D=Monthly+membership&thrivecart%5Border%5D%5B1%5D%5Bp%5D=799&thrivecart%5Border%5D%5B1%5D%5Bq%5D=1&thrivecart%5Border%5D%5B1%5D%5Bpo%5D=60120&thrivecart%5Border_currency%5D=GBP&thrivecart%5Border_id%5D=1041278&thrivecart%5Border_tax%5D=0.2&thrivecart%5Border_tax_id%5D=gb&thrivecart%5Border_total%5D=799&thrivecart%5Bpayment_processor%5D=paypal&thrivecart%5Bproduct_id%5D=6&thrivecart%5Bpurchases%5D%5B0%5D=6&thrivecart%5Bpurchases%5D%5B1%5D=6&thrivecart_hash=a5b711d2288b4cb587511811bc0a3473
So far I've set up a simple controller which doesn't get hit:
#RestController
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = "/welcome", method = RequestMethod.POST)
public void welcomeMember(#PathVariable String data) {
System.out.println(data);
}
}
How do I deal with crazy data like this? Do I have to specific each path param?
First of all, what you seem to get are not path elements but request parameters, so you will need #RequestParam annotations to get the values.
Since there are so many request parameters, I would also recommend to take just one parameter, a Map<String, String>. That Map will contain all the parameters as key/value pairs, for example:
key: "thrivecart[account_id]"
value: "3196"
If you're not sure whether you receive a POST or a GET request, you can also add a second parameter to receive the HttpMethod.
Change your RestController to:
#RestController
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = "/welcome")
public void welcomeMember(#RequestParam Map<String, String> data, HttpMethod method) {
System.out.println(method);
System.out.println(data);
}
}
That looks like a problem with how the rest api is called from the service consumer side.
try sending the data in a request body rather then as a param. This way you can use a POJO to handle the data.
Question 1: So far I've set up a simple controller which doesn't get hit:
As per your URL http://localhost:5000/user/welcome "user" seems to be your projects context name. Try removing #RequestMapping("/user") from your class.
Also, instead of#PathVariable String data use #RequestParam Map<String,String> params. #PathVariable String data is used when data is part of url but in your case it's parameter. Final code should be like below.
#RestController
public class UserController {
#RequestMapping(value = "/welcome", method = RequestMethod.POST)
public void welcomeMember(#RequestParam Map<String,String> params ) {
for(Map.Entry<String, String> entry : params.entrySet()){
//This will print all paramters name and their value
System.out.println(entry.getKey() + "-" + entry.getValue());
}
}
}
Question 2: How do I deal with crazy data like this? Do I have to specific each path param?
I will suggest to follow standard practice. Send data in json format. There are different ways for this depend upon front end technology you are using. One way is Link
My question is essentially a follow-up to this question.
#RestController
public class TestController
{
#RequestMapping("/getString")
public String getString()
{
return "Hello World";
}
}
In the above, Spring would add "Hello World" into the response body. How can I return a String as a JSON response? I understand that I could add quotes, but that feels more like a hack.
Please provide any examples to help explain this concept.
Note: I don't want this written straight to the HTTP Response body, I want to return the String in JSON format (I'm using my Controller
with RestyGWT which requires the response to be in valid JSON
format).
Either return text/plain (as in Return only string message from Spring MVC 3 Controller) OR wrap your String is some object
public class StringResponse {
private String response;
public StringResponse(String s) {
this.response = s;
}
// get/set omitted...
}
Set your response type to MediaType.APPLICATION_JSON_VALUE (= "application/json")
#RequestMapping(value = "/getString", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
and you'll have a JSON that looks like
{ "response" : "your string value" }
JSON is essentially a String in PHP or JAVA context. That means string which is valid JSON can be returned in response. Following should work.
#RequestMapping(value="/user/addUser", method=RequestMethod.POST)
#ResponseBody
public String addUser(#ModelAttribute("user") User user) {
if (user != null) {
logger.info("Inside addIssuer, adding: " + user.toString());
} else {
logger.info("Inside addIssuer...");
}
users.put(user.getUsername(), user);
return "{\"success\":1}";
}
This is okay for simple string response. But for complex JSON response you should use wrapper class as described by Shaun.
In one project we addressed this using JSONObject (maven dependency info). We chose this because we preferred returning a simple String rather than a wrapper object. An internal helper class could easily be used instead if you don't want to add a new dependency.
Example Usage:
#RestController
public class TestController
{
#RequestMapping("/getString")
public String getString()
{
return JSONObject.quote("Hello World");
}
}
You can easily return JSON with String in property response as following
#RestController
public class TestController {
#RequestMapping(value = "/getString", produces = MediaType.APPLICATION_JSON_VALUE)
public Map getString() {
return Collections.singletonMap("response", "Hello World");
}
}
Simply unregister the default StringHttpMessageConverter instance:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
/**
* Unregister the default {#link StringHttpMessageConverter} as we want Strings
* to be handled by the JSON converter.
*
* #param converters List of already configured converters
* #see WebMvcConfigurationSupport#addDefaultHttpMessageConverters(List)
*/
#Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(c -> c instanceof StringHttpMessageConverter);
}
}
Tested with both controller action handler methods and controller exception handlers:
#RequestMapping("/foo")
public String produceFoo() {
return "foo";
}
#ExceptionHandler(FooApiException.class)
public String fooException(HttpServletRequest request, Throwable e) {
return e.getMessage();
}
Final notes:
extendMessageConverters is available since Spring 4.1.3, if are running on a previous version you can implement the same technique using configureMessageConverters, it just takes a little bit more work.
This was one approach of many other possible approaches, if your application only ever returns JSON and no other content types, you are better off skipping the default converters and adding a single jackson converter. Another approach is to add the default converters but in different order so that the jackson converter is prior to the string one. This should allow controller action methods to dictate how they want String to be converted depending on the media type of the response.
I know that this question is old but i would like to contribute too:
The main difference between others responses is the hashmap return.
#GetMapping("...")
#ResponseBody
public Map<String, Object> endPointExample(...) {
Map<String, Object> rtn = new LinkedHashMap<>();
rtn.put("pic", image);
rtn.put("potato", "King Potato");
return rtn;
}
This will return:
{"pic":"a17fefab83517fb...beb8ac5a2ae8f0449","potato":"King Potato"}
Make simple:
#GetMapping("/health")
public ResponseEntity<String> healthCheck() {
LOG.info("REST request health check");
return new ResponseEntity<>("{\"status\" : \"UP\"}", HttpStatus.OK);
}
Add produces = "application/json" in #RequestMapping annotation like:
#RequestMapping(value = "api/login", method = RequestMethod.GET, produces = "application/json")
Hint: As a return value, i recommend to use ResponseEntity<List<T>> type. Because the produced data in JSON body need to be an array or an object according to its specifications, rather than a single simple string. It may causes problems sometimes (e.g. Observables in Angular2).
Difference:
returned String as json: "example"
returned List<String> as json: ["example"]
Add #ResponseBody annotation, which will write return data in output stream.
This issue has driven me mad: Spring is such a potent tool and yet, such a simple thing as writing an output String as JSON seems impossible without ugly hacks.
My solution (in Kotlin) that I find the least intrusive and most transparent is to use a controller advice and check whether the request went to a particular set of endpoints (REST API typically since we most often want to return ALL answers from here as JSON and not make specializations in the frontend based on whether the returned data is a plain string ("Don't do JSON deserialization!") or something else ("Do JSON deserialization!")). The positive aspect of this is that the controller remains the same and without hacks.
The supports method makes sure that all requests that were handled by the StringHttpMessageConverter(e.g. the converter that handles the output of all controllers that return plain strings) are processed and in the beforeBodyWrite method, we control in which cases we want to interrupt and convert the output to JSON (and modify headers accordingly).
#ControllerAdvice
class StringToJsonAdvice(val ob: ObjectMapper) : ResponseBodyAdvice<Any?> {
override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean =
converterType === StringHttpMessageConverter::class.java
override fun beforeBodyWrite(
body: Any?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class<out HttpMessageConverter<*>>,
request: ServerHttpRequest,
response: ServerHttpResponse
): Any? {
return if (request.uri.path.contains("api")) {
response.getHeaders().contentType = MediaType.APPLICATION_JSON
ob.writeValueAsString(body)
} else body
}
}
I hope in the future that we will get a simple annotation in which we can override which HttpMessageConverter should be used for the output.
Simple and Straightforward send any object or return simple List
#GetMapping("/response2")
#ResponseStatus(HttpStatus.CONFLICT)
#ResponseBody List<String> Response2() {
List<String> response = new ArrayList<>(Arrays.asList("Response2"));
return response;
}
I have added HttpStatus.CONFLICT as Random response to show how to pass RequestBody also the HttpStatus
Annotate your method with the #ResponseBody annotation to tell spring you are not trying to render a view and simple return the string plain
How can I easily separate JSON values that are sent in the same request?
Given that I POST a JSON to my server:
{"first":"A","second":"B"}
If I implement the following method in the Controller:
#RequestMapping(value = "/path", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void handleRequest(#RequestBody String input) {
// ...
}
then the input parameter will constitute a String with the entire JSON object, {"first":"A","second":"B"}. What I really want is two separate Strings (or a String and an int whichever is suitable for the particular request) with just the two values (other key / value pairs that the client may send should be ignored).
If the strings were sent as request parameters instead of JSON request body it would be simple:
#RequestMapping(value = "/path", method = RequestMethod.POST)
public void handleRequest(#RequestParam("first") String first,
#RequestParam("second") String second) {
// ...
}
I know that I can create a simple bean class that can be used in conjunction with the #RequestBody annotation that will contain both A and B when used, but it seems like a detour, since they will have different purposes inside the web app.
Dependencies:
org.springframework : spring-web : 3.1.0.RELEASE
org.codehaus.jackson : jackson-mapper-asl : 1.9.3
POJO
public class Input {
private String first;
private String second;
//getters/setters
}
...and then:
public void handleRequest(#RequestBody Input input)
In this case you need Jackson to be available on the CLASSPATH.
Map
public void handleRequest(#RequestBody Map<String, String> input)
I have written a custom WebArgumentResolver that does exactly this, combined with a custom annotation.
I don't have the source available to me now, but basically I annotated my method like this:
#RequestMapping(value = "/path", method = RequestMethod.POST)
public void handleRequest(#JsonField("first") String first, #JsonField("second") String second) {
// ...
}
Then my JsonFieldWebArgumentResolver checks if the method parameter is annotated with JsonField, and if it is it extracts the actual type from the parameter (not quite straight-forward it turns out if you want to handle generic parameters as well, such as List<String> or List<Pojo>), and invokes Jackson's JsonParser manually to create the correct type. It's a shame I can't show you any code, but that's the gist of it.
However, that solution is for Spring MVC 3.0, and if you are using 3.1 I think you will be better off using a custom HandlerMethodArgumentResolver instead. But the idea should be the same.
I'm running a webapp in Spring Web MVC 3.0 and I have a number of controller methods whose signatures are roughly as follows:
#RequestMapping(value = "/{level1}/{level2}/foo", method = RequestMethod.POST)
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#RequestParam("foo_name") String fooname,
#RequestParam(value = "description", required = false) String description);
I'd like to add some validation - for example, description should be limited to a certain length or fooname should only contain certain characters. If this validation fails, I want to return a message to the user rather than just throw some unchecked exception (which would happen anyway if I let the data percolate down to the DAO layer). I'm aware of JSR303 but have not worked with it and don't quite understand how to apply it in a Spring context.
From what I understand, another option would be to bind the #RequestBody to an entire domain object and add validation constraints there, but currently my code is set up to accept individual parameters as shown above.
What is the most straightforward way to apply validation to input parameters using this approach?
This seems to be possible now (tried with Spring 4.1.2), see https://raymondhlee.wordpress.com/2015/08/29/validating-spring-mvc-request-mapping-method-parameters/
Extract from above page:
Add MethodValidationPostProcessor to Spring #Configuration class:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
Add #Validated to controller class
Use #Size just before #RequestParam
#RequestMapping("/hi")
public String sayHi(#Size(max = 10, message = "name should at most 10 characters long") #RequestParam("name") String name) {
return "Hi " + name;
}
Handle ConstraintViolationException in an #ExceptionHandler method
There's nothing built in to do that, not yet anyway. With the current release versions you will still need to use the WebDataBinder to bind your parameters onto an object if you want automagic validation. It's worth learning to do if you're using SpringMVC, even if it's not your first choice for this task.
It looks something like this:
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#Valid #ModelAttribute() FooWrapper fooWrapper,
BindingResult errors) {
if (errors.hasErrors() {
//handle errors, can just return if using Spring form:error tags.
}
}
public static class FooWrapper {
#NotNull
#Size(max=32)
private String fooName;
private String description;
//getset
}
If you have Hibernate Validator 4 or later on your classpath and use the default dispatcher setup it should "Just work."
Editing since the comments were getting kind of large:
Any Object that's in your method signature that's not one of the 'expected' ones Spring knows how to inject, such as HttpRequest, ModelMap, etc, will get data bound. This is accomplished for simple cases just by matching the request param names against bean property names and calling setters. The #ModelAttribute there is just a personal style thing, in this case it isn't doing anything. The JSR-303 integration with the #Valid on a method parameter wires in through the WebDataBinder. If you use #RequestBody, you're using an object marshaller based on the content type spring determines for the request body (usually just from the http header.) The dispatcher servlet (AnnotationMethodHandlerAdapter really) doesn't have a way to 'flip the validation switch' for any arbitrary marshaller. It just passes the web request content along to the message converter and gets back a Object. No BindingResult object is generated, so there's nowhere to set the Errors anyway.
You can still just inject your validator into the controller and run it on the object you get, it just doesn't have the magic integration with the #Valid on the request parameter populating the BindingResult for you.
If you have multiple request parameters that need to be validated (with Http GET or POST). You might as well create a custom model class and use #Valid along with #ModelAttribute to validate the parameters. This way you can use Hibernate Validator or javax.validator api to validate the params. It goes something like this:
Request Method:
#RequestMapping(value="/doSomething", method=RequestMethod.GET)
public Model dosomething(#Valid #ModelAttribute ModelRequest modelRequest, BindingResult result, Model model) {
if (result.hasErrors()) {
throw new SomeException("invalid request params");
}
//to access the request params
modelRequest.getFirstParam();
modelRequest.getSecondParam();
...
}
ModelRequest class:
class ModelRequest {
#NotNull
private String firstParam;
#Size(min = 1, max = 10, message = "You messed up!")
private String secondParam;
//Setters and getters
public void setFirstParam (String firstParam) {
this.firstParam = firstParam;
}
public String getFirstParam() {
return firstParam;
}
...
}
Hope that helps.