I am trying to learn Spring Boot and working on a simple REST API which I develop a simple calculator.
I have a service class annotated with #Service and it has a simple method
public double calculate(String operation, double num1, double num2) {
...
}
And I am calling this method from the controller like
#GetMapping("/calculate")
public double calculate(#RequestParam(value = "op") String op,
#RequestParam(value = "num1") double num1,
#RequestParam(value = "num2") double num2) {
return calculatorService.calculate(op, num1, num2);
}
I want to validate these parameters(Whether num1 and num2 are numeric and all parameters should be not null). What is the best practice to validate the request parameters for Spring Boot?
It depends on what validation you want to perform.
One way would be to use validation annotations on the parameters themselves.
For example, #NotNull, #Min, #Max, etc. These particular ones can be found in javax.validation:validation-api, but there are many others (and you can build your own).
#GetMapping("/calculate")
public double calculate(#RequestParam(value = "op") #NotNull String op,
#RequestParam(value = "num1") double num1,
#RequestParam(value = "num2") double num2) {
The #NotNull validation will ensure that op is not null - you might want to use a #NotEmpty or #NotBlank annotation to ensure that the actual value of op has a length > 0 or a trimmed length > 0 respectively.
As Cristian Colorado points out in their comment below, Spring will automatically fail validation (or endpoint matching) if either num1 or num2 are not valid double values.
As you have told that you want to validate only Number #NumberFormat Annotation is there. The #NumberFormat annotation applies to subclasses of java.lang.Number (Integer, Float, Double, BigDecimal, etc.)
#NumberFormat(style=Style.NUMBER)
Related
I need to call a PUT or POST request with a payload
{
name:"dummy",
marks:"90.50"
}
And I have the following entity:
#Entity(name="Student")
public class Student {
private String name;
#Min(value = 0, message = "Marks should be positive.")
#Digits(integer = 2, fraction = 2, message = "value must be positive and 2 digits whole number")
private Float marks;
}
If I call API with marks as negative it gives me an appropriate error saying "Marks should be positive." because of the #Max annotation.
Similar to it, how can I show an appropriate message if I call API with invalid marks like "90.abc" or "abcd".
PS: I already have validation on the frontend, but need validation in the backend as well.
I´d say your marks property is already a Float. So how should it be possible to pass a String to this property? Isn´t it possible to directly design the API to have a number type for the JSON value type of marks?
If you really need the validation if a String is a number, it can be done with a #Pattern, e.g. #Pattern(regexp = "^\\d+(?:\\.\\d+)?\$").
I'm using Jackson JSON parser. I have simple data transfer object which should be returned via REST service.
public class PersonDto {
private String name;
private Integer age; // int?
public PersonDto(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
}
Should I favor wrapper classes over primitive types as fields in such case? Which of these approaches has more advantages, except that wrapper is nullable?
Wrapper class: java.lang.Integer
Pros:
Allows null value, therefore giving the chance to the user to leave a blank / non-specified field
Fast when values are between -128 and 127, as it uses its internal cache instead of creating new objects
Integer::valueOf for parsing String's
Cons:
Immutable: if you have to reset the value (e.g: because it exceeded a certain range), you'll have to re-create a whole new Integer instance.
Slower calculation performance
java.lang.Integer is a reference to an instance. Each calculation will recreate a whole new Object
Primitive type: int
Pros:
Mutable (as long as non-final, and a setter is provided): easily allows: this.age = age > MAX_AGE ? blabla : age;
primitive types calculations are faster
Integer::parseInt for parsing String's
Cons:
Non-selected values will be automatically set to 0; this may create confusion on whether the user selected 0 as value, or did not select anything
It seems the only advantage of java.lang.Integer is the blank / null value.
Also, in cases where a wrapper is needed, e.g:
List<Integer> myList;
you can directly add an unboxed value myList.add(2); which is interpreted by the compiler as myList.add(Integer.valueOf(2));
I found using wrapper in DTO's beneficial. With Jackson, for nullable fields you can set #JsonInclude(JsonInclude.Include.NON_NULL) on top of the DTO object and reduce the number of data sent via network (null fields will not be present in resulting JSON), thus resolving ambiguity if value is 0 or not present, telling the front-end that no node is present and hence no processing/displaying data is needed.
For non-nullable numeric data, primitive works well.
Also, for floating-point data that are not supposed to be used in front-end in arithmetic calculations, one can use String with rounding performed on server-side. I saw this technique multiple times in REST API's.
DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
System.out.println(df.format(d));
I'm new to spring boot and learning #RequestParam()
I know that we can give defaultValue in String but when I am trying to give default value as Integer it's showing me an error.
#RequestMapping("/returnVeriable")
public int getVeriable(#RequestParam(required=true,defaultValue=1/*error here*/) int veri){
return veri;
}
any help would be appreciated.
Try with "" around integer to make it string as the defaultValue is implemented as String.
#RequestMapping("/returnVeriable")
public int getVeriable(#RequestParam(required=true,defaultValue="1") Integer veri){
return veri;
}
refer : https://jira.spring.io/browse/SPR-5915
When a value traffic by HTTP protocol, it has no type. It's a String. And so it is at this parameter type at the annotation. Then, you do something like this:
#RequestParam(required = true, defaultValue = "1") Integer veri
And it will work fine.
This should work
#RequestMapping("/returnVariable")
public int getVariable(#RequestParam(required=true,defaultValue="1") int var) {
return var;
}
By default what ever you pass to the controller is treated as String and converted to respective types. So even default values need to be set as String
How to validate a property that should be grater than zero?and not zero.I used built in annotation #Min(0) but how can i ignore the zero?Is there any other built in annotation for this case?
#Min(0)
default public Double getAmd1() {
return (Double) get("amd1");
}
Check out the #DecimalMin annotation
It provides a boolean parameter inclusive which fits your needs.
Sadly Double is not supported because of rounding issues, but BigDecimal is. You can then get the value as double by using getDouble().
So you can try something like this:
#DecimalMin(value = "0.0" ,inclusive = false)
private BigDecimal amd1;
public BigDecimal getAmd1() {
Double d = (Double) get("amd1");
return BigDecimal.valueOf(d);
}
In Java, new BigDecimal("1.0") != new BigDecimal("1.00") i.e., scale matters.
This is apparently not true for Hibernate/SQL Server, however. If I set the scale on a BigDecimal to a particular value, save the BigDecimal to the database via Hibernate and then re-inflate my object, I get back a BigDecimal with a different scale.
For instance, a value of 1.00 is coming back as 1.000000, I assume because we're mapping BigDecimals to a column defined as NUMERIC(19,6). I can't just define the column as the required scale as I need to store both Dollar and Yen values (for example) in the same column. We need to represent the BigDecimals as numeric types in the database for the benefit of external reporting tools.
Does there exist a Hibernate UserType which maps BigDecimal "properly", or do I have to write my own?
Just for informational sake, I can tell you that the creation of the BigDecimal coming back from the database is done by the proprietary JDBC driver's implementation of the 'getBigDecimal' method of the database-specific 'ResultSet' sub-class.
I found this out by stepping thru the Hibernate source code with a debugger, while trying to find the answer to my own question.
I think this will work, I didn't test it though.
public class BigDecimalPOJO implements Serializable {
private static final long serialVersionUID = 8172432157992700183L;
private final int SCALE = 20;
private final RoundingMode ROUNDING_MODE = RoundingMode.CEILING;
private BigDecimal number;
public BigDecimalPOJO() {
}
public BigDecimal getNumber() {
return number.setScale(SCALE, ROUNDING_MODE);
}
public void setNumber(BigDecimal number) {
this.number = number.setScale(SCALE, ROUNDING_MODE);
}
}
Not sure, but you can check equality using a.compareTo(b) == 0.