I have a problem how messages are resolved using hibernate annotations in Spring forms.
I have following method in the controller:
#RequestMapping(value = "/path", method = RequestMethod.POST)
public CustResponse storeForm(HttpServletResponse response, HttpServletRequest request,
#Valid #RequestBody Form form, BindingResult result) {
And when request comes I want to resolve all error messages into CustResponse object. In order to resolve warning message I am using following method from BindingResult result:
result.getCode();
According to documentation it:
Return the default code of this resolvable, that is, * the last
one in the codes array.
And default code are is resolved is very general: NotBlank for this annotation:
import org.hibernate.validator.constraints.NotBlank;
However it also exists other more specific error codes in the BindingResult result. As example:
[NotBlank.form.fieldNAme, NotBlank.fieldNAme, NotBlank.java.lang.String, NotBlank]
And I think it makes more sense to resolve error message using most meaningful error code:
NotBlank.form.fieldNAme
In order to resolve warning message I don't want to iterate through all error codes.
How would you suggest to cope with such kind of issue ?
Related
I have written API's which are resulting in 500 error when hit through postman or browser. However, when I debug and see server is not throwing any error and in fact returning a proper response. Other controller I have implemented in a similar way is returning expected result. below is my controller code. Has anyone faced similar situation. Kindly help.
#CrossOrigin
#GetMapping(value="/byPatientId/{patientId}", produces = "application/json")
public List<ContactInfo> getAllContacts(#PathVariable String patientId) {
logger.info("Received request for List of ContactInfo for patientId: "+patientId);
List<ContactInfo> list =
contactInfoService.getAllContacts(patientId);
return list;
}
#CrossOrigin
#GetMapping("/byContactId/{contactId}")
public ContactInfo getContactById(#PathVariable Integer contactId) {
logger.info("Received request for ContactInfo for contactId: "+contactId);
return contactInfoService.getContactById(contactId);
}
The problem was with one of the dependent object which was having oneToMany relationship with the return type object and it was set to Lazy loading and issue was during the serialization.
Either we can change it to Eager loading or ignore the dependent object by adding #JsonIgnore on dependent object.
I handled it by adding #JsonIgnore annotation on top of the dependent object as I don't need the dependent object in this particular usecase. Issue is solved now.
How is your Controller annotated? is it with #Controller or #Rest?
#RestController = #Controller + #ResponseBody(for serializing the response and pass it into the HttpResponse.
Add the #ResponseBody in your methods on the controller or change the #Controller tag into a #RestController(take into account that #RestController is available since 4.0 Spring version).
More info:https://www.baeldung.com/spring-controller-vs-restcontroller
I am new to Spring and I am trying to do the basic GET and POST method.
This is how I am trying to do the methods:
#RestController
public class DeskController {
#Autowired
private DeskDao dao;
#GetMapping("desks")
public List<Desk> getDesks() {
System.out.println(dao.findById(1L));
return dao.findAll();
}
#PostMapping("desks")
public Desk save(#RequestBody #Valid Desk desk) {
Desk deskObj = dao.save(desk);
System.out.println(deskObj);
return deskObj;
}
When I am calling the POST method like this I get the pring with the actual object that I had called it with so it is working fine, but I also get this error:
javax.ws.rs.ProcessingException: Content-Type is missing
And when trying to call GET it tells me that:
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
I am aware that I have not included the whole code, but I will add what is needed to resolve this error since there are a lot of classes.
My questions are, what do I do against the first error and why is GET method not supported?
Two things you need to change :
Use a / to indicate that for this path you will perform an
operation. For eg : (/desks)
Use the annotation #Consumes to
indicate that this method accepts the payload in particular format. For eg : #Consumes(MediaType.APPLICATION_JSON) annotated over your save() method.
I have a spring boot controller endpoint as follows.
#PutMapping("/manage/{id}")
public ResponseEntity<Boolean> manage(#PathVariable Long id, #RequestBody Type type) {
...
}
Where Type is an Enum as follows.
public enum Type {
ONE,
TWO
}
ISSUE 1: When I test this controller, I have to send the content as "ONE" instead of ONE for a successful invocation. i.e. it works with the following code.
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("\"" + Type.ONE + '\"'))
.andExpect(status().isOk());
It does not work with
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(Type.ONE.name()))
.andExpect(status().isOk());
ISSUE 2: I am not able to invoke this method from the Angular service.
this.http.put<string>('/api/manage/' + id, type)
gives me
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
Everything works when I add the Enum to a Dto and send an object from the client. But due to some business requirements, I want to use the current structure itself. i.e the Enum as a RequestBody.
UPDATE
I also tried to change the controller method structure to
#PutMapping(value = "/manage/{id}", consumes = MediaType.TEXT_PLAIN_VALUE)
I get the following error.
Content type 'text/plain' not supported
Both issues stem from trying to use a JSON endpoint as a plain text endpoint.
Ad 1, ONE is invalid JSON ("ONE" is valid)
Ad 2, when you just post a string, it is sent as text/plain and the endpoint complains.
Probably adding consumes="text/plain" to your #PutMapping will solve the problem, but frankly - I am not sure if string/enum mappings work out-of-the-box in the hodge-podge that is spring boot.
I am working on a Spring REST application.
This application has only REST controllers, no view part.
I want to know how can I validate a #RequestParam
For example
#RequestMapping(value = "", params = "from", method = RequestMethod.GET)
public List<MealReadingDTO> getAllMealReadingsAfter(#RequestParam(name = "from", required = true) Date fromDate) {
......
......
}
In the above example, my goal is to validate the Date. Suppose someone pass an invalid value, then I should be able to handle that situation.
Now it is giving and exception with 500 status.
PS
My question is not just about Date validation.
Suppose, there is a boolean parameter and someone passes tru instead of true by mistake, I should be able to handle this situation as well.
Thanks in advance :)
Spring will fail with an 500 status code, because it cannot parse the value.
The stages of request handling are:
receive request
identify endpoint
parse request params / body values and bind them to the detected objects
validate values if #Validated is used
enter method call with proper parameters
In your case the flow fails at the parse (3) phase.
Most probably you receive a BindException.
You may handle these cases by providing an exception handler for your controller.
#ControllerAdvice
public class ControllerExceptionHandler {
#ExceptionHandler(BindException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public YourErrorObject handleBindException(BindException e) {
// the details which field binding went wrong are in the
// exception object.
return yourCustomErrorData;
}
}
Otherwise when parsing is not functioning as expected (especially a hussle with Dates), you may want to add your custom mappers / serializers.
Most probably you have to configure Jackson, as that package is responsible for serializing / deserializing values.
Iv'e seen this nice mechanism:
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-jsr303-valid-example/
Is it possible to make the #Valid annotation avaialble for all the Controllers with validation? It seems very redundant to do the following:
#RequestMapping(value = "/getPlayerAccounts", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
#ResponseBody
public QueryResultsDTO<PlayerAccountResultDTO> getPlayerAccounts(#RequestBody **#Valid** FilteredQueryRequestDTO filteredQueryRequestDTO,
**BindingResult result**) {
**this.validateDTO(result);**
return this.playerService.getPlayerAccounts(filteredQueryRequestDTO);
}
Reduandant code:
#Valid
BindingResult result
this.validateDTO(result);
These seems like a recurring pattern, probably someone already solved it? maybe with aspects?
I dont care that all my methods and controllers will have the #Valid login, most of the DTOs they recieve will be valid anyway (since no validation annotations are applied to them)
Thanks
you cannot omit #Valid annotation, since this is the way to tell spring which dto to validate, that is just the way the spring validation works. But having a BindingResult result to each of your methods is not necessary. You may omit it completely. If you want to do something when validation fails, you can catch the MethodArgumentNotValidException that is thrown in that case from an exception handling method (e.g you can use a class with #ControllerAdvice annotations that will contain #ExceptionHandler methods applied to all controllers - exception handling is a whole different topic, you can read more details on related spring mvc exception handling documentation)