I have a Spring MVC application which communicates with the frontend with AJAX / JSON, and I have in the frontend a web application with HTML.
For adding an element to the database, I do this in the backend system:
#RequestMapping(value="add/", method=RequestMethod.POST)
public #ResponseBody SerializePerson addProject(#RequestBody Person person) {
Person p = this.personService.addPerson(person);
return new SerializePerson(p.getId(), p.getName(), p.getEmail());
}
But now I have the problem (this is a very simple example), that someone can create a project without a name, so the name = "" and a non valid email address.
My problem is, that I want to validate the fields in the backend system.
So I found a Spring MVC showcase here: https://src.springsource.org/svn/spring-samples/mvc-showcase/src/main/java/org/springframework/samples/mvc/validation/
They do this:
#RequestMapping("/validate")
public #ResponseBody String validate(#Valid JavaBean bean, BindingResult result) {
if (result.hasErrors()) {
return "Object has validation errors";
} else {
return "No errors";
}
}
So, is this the best way? So I have to do two steps:
validate the Person object (if no errors occur, go to step 2, otherwise show error message to user)
write the Person object to the datbase
Isn't it possible to combine these two steps in one step? And how can I put the Person POST object from the frontend to the "validate" method in the backend and to see which field fails (name or email), because telling only "Object has validation errors" is not so good :-)?
Best Regards.
I did it shown in this example: http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
#RequestMapping(method=RequestMethod.POST)
public #ResponseBody Map<String, ? extends Object> create(#RequestBody Account account, HttpServletResponse response) {
Set<ConstraintViolation<Account>> failures = validator.validate(account);
if (!failures.isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return validationMessages(failures);
} else {
accounts.put(account.assignId(), account);
return Collections.singletonMap("id", account.getId());
}
}
Related
I want to inject into the new object a class that contains the user.
#GetMapping
public String listTopic(Principal principal, Model model){
Optional<Users> user = usersService.findByUsername(principal.getName());
if (user.isPresent()){
Topics topic = new Topics();
topic.setUsers(user.get());
model.addAttribute("newTopic", topic);
model.addAttribute("topics", topicsService.listTopics());
return "forum/forum";
}
return "/error";
}
#PostMapping
public String addTopic(#Valid #ModelAttribute("newTopic") Topics topic, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "forum/forum";
}
topicsService.addTopic(topic);
System.out.println(topic);
return "redirect:/forum";
}
When I pass sysout after setting user obect or adding attribute at getmapping section it shows me the exact object, but when I want to see it at the postmapping it throws nullpointerexception.
Your model is a request scope object. After each request it is lost. You need to pass this information to a session object that is alive through different requests in the same session
https://stackoverflow.com/a/18795626/7237884
I have a requirement to implement an HTTP PATCH method in a Spring MVC application. I followed this tutorial: https://www.baeldung.com/http-put-patch-difference-spring.
This is the piece of code:
#RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> partialUpdateGeneric(
#RequestBody Map<String, Object> updates,
#PathVariable("id") String id) {
heavyResourceRepository.save(updates, id);
return ResponseEntity.ok("resource updated");
}
The problem is that my repository (JPARepository) does not have a method "save" where I can pass a map and an id.
I tried this implementation on my own:
#PatchMapping("/heavyresource/{id}")
public Beer patchUpdate(#RequestBody HeavyResource heavyResource) {
return heavyResourceRepository.save(heavyResource);
}
But it does not work properly because if I pass only one property (that's the point in PATCH) it let's all the others properties as null and I need to update only the property that was passed. Even thinking in DTOs I was not able to implement.
Thanks!
In spring doc, I can get following explanations for the difference of the spring mvc and spring rest.
Spring REST architecture is also based on Spring MVC, slightly making the difference on the View part. Traditional Spring MVC relies on the View technology to render the model data, the Spring REST architecture also does the same, except that the model object is set directly into the HTTP response, which the #ResponseBody converts into JSON/XML automatically. The output of a RESTful web service has to be a JSON or an XML, a standard format that could be easily handled across different consumer application platforms.
But in https://en.wikipedia.org/wiki/Representational_state_transfer.
It has a couple of feactures except for the json response like the rest will use the HTTP PUT/DELETE/POST method to manipulate resource.
I was wondering if below spring controller can be treated as a restful service. I have used #RestController to return json response, but did not use any other rest features.
#RestController
#RequestMapping(value = "/employee")
public class EmployeeController {
#RequestMapping(value = RequestAction.LOADLIST, method = RequestMethod.POST)
public List<Employee> list(#RequestBody Employee bo) {
System.out.println(bo);
return employeeList;
}
#RequestMapping(value = RequestAction.LOAD, method = RequestMethod.POST)
public Employee getEmployee(
#RequestBody Employee input) {
for (Employee employee : employeeList) {
if (employee.getId().equals(input.getId())) {
return employee;
}
}
return input;
}
#RequestMapping(value = RequestAction.ADD, method = RequestMethod.POST)
public Employee addEmployee(#RequestBody Employee bo) {
System.out.println(bo);
return bo;
}
#RequestMapping(value = RequestAction.UPDATE, method = RequestMethod.POST)
public Employee updateEmployee(#RequestBody Employee bo) {
System.out.println(bo);
for (Employee employee : employeeList) {
if (employee.getId().equals(bo.getId())) {
employee.setName(bo.getName());
return employee;
}
}
return bo;
}
}
Your example script is not REST because it change the url for each task, and use always POST verb. Spring REST use different HTTP verbs (GET, POST, DELETE) to differentiate the action. A few times sharing the same url.
Example:
#RestController
#RequestMapping("/users")
public class UsersController {
#GetMapping
public List<User> index() {...}
#GetMapping("{id}")
public User show(...) {...}
#PostMapping
public User create(...) {...}
#PutMapping("{id}")
public User update(...) {...}
#DeleteMapping("{id}")
public void delete(...) {...}
}
Your example is not following the conventionals of a REST API (e.g. GET for retrieval, POST for create, PUT for full update, PATCH for partial update, etc.), but it does not mean, that you can't. As others stated above, you might just got confused with the term. REST is a protocol and it has lots of conventionals for service usages, which if you follow, you can say that your service is REST or RESTful.
This page is the simple best source of tutoring you in this area:
https://restfulapi.net
More importantly this, when we are considering your example: https://restfulapi.net/http-methods/
I also check it sometimes.
I was struggling to get my Spring MVC validation to return to the page submitted page when I had errors. I finally solved the problem by noticing that BindingResult needs to be next to form parameter I'm validating.
For example if I amend the checkPersonInfo method in the spring.io tutorial(http://spring.io/guides/gs/validating-form-input/) to -
#RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(#Valid Person person, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
Then it will work and redirect to the form page, but if I change it to -
#RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(#Valid Person person, Model model, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
Then it redirects to /errors
What is the cause of this?
The BindingResult has to follow the object that is bound. The reason is that if you have more objects that are bound you must know which BindingResult belongs to which object.
Yeah, Today I took a long time to check why cannot back to the submitted page but goes to a default whitelable error page.
After debugging got the source code
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
if BindingResult does not follow #Valid , causes isBindExceptionRequired(binder, parameter) return true and then directly throw exception so cannot execute code in controller method.
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor#isBindExceptionRequired
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
int i = methodParam.getParameterIndex();
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
You can potentially have multiple model attributes in your request handler, each with their own binding result. To accomodate this, Spring decided to bind binding result parameters to the previous paramater.
I am building an ecommerce clothing application. I am trying to preserve the errors after a redirect. I should note I had this working with tomcat as my web server, I am migrating to Google App Engine and am getting serialization issues.
In my controller I have a method like this
#Autowired
#Qualifier("productDetailValidator")
private Validator validator;
#InitBinder("productDetail")
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
#RequestMapping(value="/add-to-cart", method = RequestMethod.POST)
public String submitForm(#RequestParam("id") Integer productId,
#ModelAttribute("productDetail") #Validated ProductDetail productDetail, BindingResult result,
RedirectAttributes redir, Principal auth, Model model) {
String view = null;
User user = null;
Product product = null;
if(result.hasErrors()) {
user = userService.findUser(name);
product = productService.findProduct(productId);
redir.addFlashAttribute("org.springframework.validation.BindingResult.productDetail", result);
redir.addFlashAttribute(...,...); // I add about 5 attributes to the flashmap besides BindingResult
return "redirect:/product";
}
}
Besides the binding result I have to preserve some details about the page like the current product being viewed, cart total, signed in user, etc
And here is my validate method inside my validator
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "quantity", "valid.quantity", "Please enter a quantity");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "size", "valid.size", "Please select a size.");
ProductDetail productDetail = (ProductDetail) obj;
if(productDetail.getColor().equalsIgnoreCase("default")){
errors.rejectValue("color", "valid.color", "Please select a color.");
}
if(productDetail.getQuantity() <= 0){
errors.rejectValue("quantity", "minimum.quantity", "Please select quantity greater than 0.");
}
}
I get this error now
java.io.NotSerializableException:org.springframework.format.support.DefaultFormattingConversionService
I cannot find much information about this issue - its pretty trivial, DefaultFormattingConversionService class does not implement Serializable, but I don't have access to that class as its part of Spring's framework.
If I take the BindingResult out of the flashmap the application works without error, when running with a debugger I can see all errors are being found, just not displayed when they are supposed to.
Thank you