#JsonProperty("test_id") do not work in springboot #GetMapping - spring-boot

My Controller code like:
#GetMapping("/test")
public TestOutputDTO getSchedule(#Valid TestInputDTO dto, BindingResult bindingResult) throws JusException {
if (bindingResult.hasErrors()) {
....
}
...
}
TestInputDTO defines like:
#Getter
#Setter
public class TestInputDTO {
#NotNull
#JsonProperty("test_id")
private Long testId;
}
http://localhost:8866/test?test_id=1 DO NOT WORK, testId is null.
http://localhost:8866/test?testId=1 WORKS
I want to call this api by test_id style.
what can i do with this?
Thakns.

Can you try this: Add #RequestBody to the dto param as follows:
#GetMapping("/test")
public TestOutputDTO getSchedule(#Valid #RequestBody TestInputDTO dto, BindingResult bindingResult) throws JusException {
...
}
If it still doesn't work, test your Jackson mapping by writing a unit test and see what's result.
#Test
public void testMapping(){
TestInputDto testInput = new TestInputDto();
testInputDto.setTestId(1L);
assertEquals("{ \"test_id\" : 1}", objectMapper.writeValueAsString(testInputDto));
}

Related

How to use #RequestParam with DTO

I have to make GET method with a DTO.
But when I code like this↓, an error occurs.
org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'param' for method parameter type SampleDTO is not present
After checking that error, I figured out I need add option #RequestParam(required=false).
Then, I restarted tomcat.
Although there was no more error, my param was null(I actually sent sample_name).
And I tried to use both of no annotation and #ModelAttribute.
Both of them occurs same error↓
Caused by: java.lang.NoSuchMethodError: org.springframework.beans.BeanUtils.getResolvableConstructor(Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
What should I do? plz give me advice.
I don't know best way handling DTO.
Because I usually coded using HashMap actually.
Here is my example code.
//Controller sample
point: insertSample method works well.
#RestController
#RequestMapping("/sample")
public class SampleController {
#Autowired
private SampleService sampleService;
#GetMapping
public Result getSampleList(#RequestParam SampleDTO param) throws Exception {
// (#RequestParam(required=false) SampleDTO param)
// (#ModelAttribute SampleDTO param)
// (SampleDTO param)
return sampleService.getFolderList(param);
}
#PostMapping
public Result insertSample(#RequestBody SampleDTO param) throws Exception {
return sampleService.insertFolder(param);
}
}
// DTO sample
#Getter // I didn't attach #Setter because of #Builder.
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
#Alias("SampleDTO")
public class SampleDTO {
#NotNull
private Long sampleNo;
#NotBlank
private String sampleName;
private String sampleDesc;
#Builder
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}
}
In order to bind request parameters to object you need to have standard getters/setters in your DTO class. Add #Setter to your method, then you can bind without even any annotation.
#GetMapping
public Result getSampleList(SampleDTO param) throws Exception {
return sampleService.getFolderList(param);
}
#GetMapping
public Result getSampleList(#RequestParam("param") SampleDTO param) throws Exception {
// (#RequestParam(required=false) SampleDTO param)
// (#ModelAttribute SampleDTO param)
// (SampleDTO param)
return sampleService.getFolderList(param);
}
}
Try like this, You should have to designate variable

GET method: How to convert snake_case query string to camelCase DTO

I use snake_case DB columns and camelCase DTO.
And our team want to use snake_case when we code React component.
Because of it, I added #JsonNaming on DTO. But it works when I send Json data, as you know.
Is there any annotation or setting similar to #JsonNaming?
Here is my postman data and sample codes.
Debug data: sampleName=name, sampleDesc=null.
// Controller
#RestController
#RequestMapping("/sample")
public class SampleController {
#Autowired
private SampleService sampleService;
#GetMapping
public Result getSampleList(SampleDTO param) throws Exception {
return sampleService.getFolderList(param);
}
#PostMapping
public Result insertSample(#RequestBody SampleDTO param) throws Exception {
// this method works well with #JsonNaming
return sampleService.insertFolder(param);
}
}
// DTO
#Setter
#Getter
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
#Alias("SampleDTO")
public class SampleDTO {
#NotNull
private Long sampleNo;
#NotBlank
private String sampleName;
private String sampleDesc;
#Builder
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}
}
I had the same problem and didn't find an annotation for this but maybe you can use #ConstructorProperties like this in your DTO's constructor:
#ConstructorProperties({"sample_no","sample_name","sample_desc"})
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}

Why does MongoRepository save return an empty json and save empty value when a variable is not empty?

I have a simple document:
#Document
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
public class ProductUnit {
#Id
String id;
private String name;
private Integer price;
private LocalDateTime localDateTime;
}
Simple MongoRepository :
public interface productRepo extends MongoRepository<ProductUnit,String> {
ProductUnit deleteByName(String name);
List<ProductUnit> findByPrice(Integer price);
}
and Service :
#Service
public class productServiseImpl implements productServise {
#Autowired
productRepo repository;
#Override
public ProductUnit saveOrUpdate(ProductUnit productUnit) {
System.out.println("inside save or update");
return repository.save(productUnit);
}
#Override
public List<ProductUnit> findAll() {
return repository.findAll();
}
#Override
public ProductUnit deleteUnitByPrice(String name) {
return repository.deleteByName(name);
}
#Override
public List<ProductUnit> findByPrice(Integer price) {
return repository.findByPrice(price);
}
}
Now , inside RestController , I pass id through a post request and use a random class to generate a random value of the price and name .At this stage everything is fine, i.e. all values were initialized correctly, but when it comes to service.saveOrUpdate(forSave) It stores the value incorrectly, i.e. the request returns an empty json and the findAll method returns a list of empty json.Can you tell me what the error is? thanks
#RestController
public class productUnitRestController {
#Autowired
productServise service;
#Autowired
Supplier<MetaInfGenerator> generatorSupplier;
#GetMapping(path = "/all")
public List<ProductUnit> getAllProoduct(){
return service.findAll();
}
#PostMapping(path = "/products")
public ProductUnit createProoduct(#RequestParam("id") Optional<String> newId){
System.out.println("***** iside PostMapping ******");
MetaInfGenerator generator = generatorSupplier.get();
System.out.println("***** supplier PostMapping ******");
ProductUnit forSave = ProductUnit.builder()
.id(newId.get())
.name(generator.getRandomString())
.price(generator.getRandomInteger())
.localDateTime(LocalDateTime.now()).build();
System.out.println(forSave);
return service.saveOrUpdate(forSave);
}
}

Spring REST Service Controller not being validate by #PathVariable and #Valid

#Controller
#EnableWebMvc
#Validated
public class ChildController extends ParentController<InterfaceController> implements InterfaceController{
#Override
#RequestMapping(value = "/map/{name}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public List<Friends> getAllFriendsByName(
#Valid
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable("name") String name,
#RequestParam(value="pageSize", required=false) String pageSize,
#RequestParam(value="pageNumber", required=false) String pageNumber,
HttpServletRequest request) throws BasicException {
//Some logic over here;
return results;
}
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
Hi, I am trying to do pretty basic validation for a spring request parameter but it just doesn't seem to call the Exception handler, could someone point me into the right direction
P.S. I keep getting NoHandlerFoundException
Spring doesn't support #PathVariable to be validated using #Valid. However, you can do custom validation in your handler method or if you insist on using #Valid then write a custom editor, convert your path variable value to an object, use JSR 303 bean validation and then use #Valid on that object. That might actually work.
Edit:
Here's a third approach. You can actually trick spring to treat your path variable as a model attribute and then validate it.
1. Write a custom validator for your path variable
2. Construct a #ModelAttribute for your path variable and then use #Validator (yes not #Valid as it doesn't let you specify a validator) on that model attribute.
#Component
public class NameValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
String name = (String) target;
if(!StringUtils.isValidName(name)) {
errors.reject("name.invalid.format");
}
}
}
#RequestMapping(value = "/path/{name}", method = RequestMethod.GET)
public List<Friend> getAllFriendsByName(#ModelAttribute("name") #Validated(NameValidator.class) String name) {
// your code
return friends;
}
#ModelAttribute("name")
private String nameAsModelAttribute(#PathVariable String name) {
return name;
}

Why can't I use Valid parameter along with RequestParam in Spring MVC?

Example:
public String getStudentResult(#RequestParam(value = "regNo", required = true) String regNo, ModelMap model){
How can I use #valid for the regNo parameter here?
Late answer. I encounter this problem recently and find a solution. You can do it as follows,
Firstly register a bean of MethodValidationPostProcessor:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
and then add the #Validated to the type level of your controller:
#RestController
#Validated
public class FooController {
#RequestMapping("/email")
public Map<String, Object> validate(#Email(message="请输入合法的email地址") #RequestParam String email){
Map<String, Object> result = new HashMap<String, Object>();
result.put("email", email);
return result;
}
}
And if user requested with a invalid email address, the ConstraintViolationException will be thrown. And you can catch it with:
#ControllerAdvice
public class AmazonExceptionHandler {
#ExceptionHandler(ConstraintViolationException.class)
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleValidationException(ConstraintViolationException e){
for(ConstraintViolation<?> s:e.getConstraintViolations()){
return s.getInvalidValue()+": "+s.getMessage();
}
return "请求参数不合法";
}
}
You can check out my demo here
#Valid can be used to validate beans. I have'nt seen it used on single string parameters. Also it requires a validator to be configured.
The #Valid annotation is part of the standard JSR-303 Bean Validation API, and is not a Spring-specific construct.
Spring MVC will validate a #Valid object after binding so-long as an appropriate Validator has been configured.
Reference : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
one way to do it is to write a Wrapper Bean like the following :
public class RegWrapperBean{
#NotNull
String regNo ;
public String getRegNo(){
return regNo ;
}
public void setRegNo(String str){
this.regNo=str;
}
}
and your handler method will be like the following :
#RequestMapping(value="/getStudentResult", method=RequestMethod.POST)
public String getStudentResult(#Valid #ModelAttribute RegWrapperBean bean,
BindingResult validationResult, Model model) {
}
and please refer to these answers here and here .
Hope that Helps .

Resources