Spring MVC and thymeleaf ModelAttribute either null or not evaluated - java

I'm developing a web application with Spring MVC and Thymeleaf as my ViewResolver. I have the following controller handler method:
#RequestMapping(value = "/something", method = RequestMethod.POST, params = "submit")
public String doSomething(#ModelAttribute("error") String error /*, other attributes */) {
// find out if there is an error
error = getErrorMessage();
return "someHTMLfile";
}
My view contains this line:
<p><span th:text="${error}">Error Message goes here</span></p>
When executed, the tag does not render to anything. This is probably due to ${error} evaluating to an empty string but I can't understand why. Doesn't Spring's #ModelAttribute annotation add the object to the model map automatically, where Thymeleaf can find it?
If I instead have:
#RequestMapping(value = "/something", method = RequestMethod.POST, params = "submit")
public String doSomething(ModelMap map /*, other attributes */) {
// find out if there is an error
String error;
error = getErrorMessage();
map.addAttribute("error", error);
return "someHTMLfile";
}
The view is rendered perfectly fine with the error message. Does #ModelAttribute not add the object to the request model?
Edit: I've tried doing both:
#RequestMapping(value = "/something", method = RequestMethod.POST, params = "submit")
public String doSomething(#ModelAttribute("error") String error, ModelMap map /*, other attributes */) {
// find out if there is an error
error = getErrorMessage();
map.addAttribute("error", error);
return "someHTMLfile";
}
This also doesn't work.

Actually I don't think your issue is related to Thymeleaf, just SpringMVC :-)
In your first snippet, you don't add anything to the request model but try to get an object called "error" back from the form.
In your second snippet, you do add an object to the model, that's why your view is well rendered.
Take a look at the SpringMVC doc here (16.3.3.8) to have a better understanding of the #ModelAttribute annotation on a method argument.

I feel stupid but whatever, we all make mistakes.
Spring was creating a new String instance for me and injecting it into my method and into the model under the key error. String is an immutable object, so when I do error = getErrorMessage(), I assign another instance to my error object. Now there is my error and the error String in Spring's model with a value of "". That's why Thymeleaf rendering only finds the empty string.

Related

Whitelabel Error Page instead of missing required parameter

I have a GET /object call with required parameter id:
#RequestMapping(value = {"/object"}, produces = "application/json", method = RequestMethod.GET)
public String getObject(#RequestParam(required = true) String id, HttpServletRequest request) {
// do stuff
...
// Send message
return json;
}
When it's called without the parameter id my spring application throws a :
Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'id' is not present] but the caller recieves a Whitelabel Error Page with a 400 status with no explanation on the missing parameter...
Is there a way to return to the caller what paramter is missing ?
You need to create a custom Error Page, which would describe the exceptions you declare. See here: https://www.baeldung.com/exception-handling-for-rest-with-spring

Receive Collection/Iterable as arguments in spring boot controller

I am new to Spring and I want to write a controller which will take Collection/Iterable as arguments. Like this:
#RequestMapping(value = "friends", method = RequestMethod.POST)
public #ResponseBody Callable<Iterable<User>>
getFriendsOfUser(#RequestParam(required = true, name = "mobiles") Iterable<String> mobs) {
// return callable
}
There is no compilation error, but I cannot make it work. Can you say how will this work? And how shall be the request to this api be constructed?
public String getFriendsOfUser(#RequestParam(required = true, value = "mobiles") String[] mobiless){
....
}
and your mobile should be
mobiles=myValue1&mobiles=myValue2&mobiles=myValue3
or
mobiles=myvalue1,myValue2,myValue3
still if you have any doubt post your front-end code and Ajax call.
You've mapped a POST method so you might need #RequestBody instead of #RequestParam
#RequestParam is, as the name implies, for request parameters: [host]/endpoint?param=foo&secondParam=bar
whereas
#RequestBody is for JSON/XML or any other type content sent as the request's body.

AngularJs how to access Model Attribute value from Spring MVC controller

I have a Spring MVC Controller returning a page with an attribute as followed
#RequestMapping(method = RequestMethod.GET, value = "/create")
public ModelAndView getAddAccountView() {
ModelAndView model = new ModelAndView("protected/accounts/AccountAddView");
List<Client> clients=clientService.findAll();
model.addObject("listClients", clients);
return model;
}
Client is a #Entity
in my AccountAddView.jsp file, i'm trying to use the ng-init as follow:
<div class="row-fluid" ng-controller="accountsController as ctrl" ng-init="clients=${listClients}">
and in my app.js, in my controller, i try to access the list of client as followed
var listOfClients=$scope.clients;
but I'm still getting the following error
angular.min-1.5.3.js:116 Error: [$parse:lexerr] http://errors.angularjs.org/1.5.3/$parse/lexerr?p0=Unexpected%20nextharacter%20&p1=s%2033-33%20%5B%40%5D&p2=clients%3D%5Bsoftbank.ui.model.Client%4042%2C%softbank.ui.model.Client%4041%2C%softbank.ui.model.Client%4043%5D
at Error (native)
at http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:6:416
at gc.throwError (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:212:149)
at gc.lex (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:211:16)
at Object.ast (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:216:103)
at Object.compile (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:225:232)
at hc.parse (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:252:380)
at e (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:123:317)
at m.$eval (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:142:463)
at pre (http://localhost:8080/softbank/resources/js/angular.min-1.5.3.js:271:500)
please what is wrong here. why is ng-init generating this errors?
thanks in advance for your answer.
I'm just starting with Angular with Spring so what I'll do is explain how I did it and you can see if it's a viable option for you.
For one, I didn't try to go through the ModelAndView for my data. I let my "regular" controller return the view but got my data via the angular controller and service along with a #RestController on the Spring side (that's two separate Spring controllers then).
To return only the view you have at least two options that I'm aware of. One, you can just return a ModelAndView without a model like so:
public ModelAndView yourView() {
return new ModelAndView("yourView");
}
Or two, return the name of the view as a string like so:
public String yourView() {
return "yourView";
}
In both cases you'd obviously need the correct #RequestMapping.
In my view I had an angular controller function that made a call to my associated angular service which in turn called my #RestController and so on. I initiated the data like so:
ng-controller="MyController as ctrl" ng-init="ctrl.allThings()"
Examples of the code:
Angular controller:
self.allThings = function () {
ThingService.things()
.then(
function (data) {
self.things = data;
},
function (errResponse) {
console.error("Error retrieving thing list");
}
);
};
Angular service:
things: function() {
return $http.get('/things')
.then(
function(response) {
return response.data;
},
function (errResponse) {
return $q.reject(errResponse);
}
);
},
Spring REST controller:
#RequestMapping(value = "/things", method = RequestMethod.GET)
public List<Thing> things() {
return thingService.findAll();
}
I imagine you know the remaining code for the Spring side. Jackson will handle all the object conversions for you.
Old Post but still I would like to answer so that anyone who is using Angular with Spring MVC get some help.
Using model attribute is a challenge with Angular. Earlier I was setting employee data in model attribute like mav.addObject("employee",employee), but when I was trying to use ${employee} inside the jsp, Angular wasn't able to set the java object using ng-init directive because it was expecting a java script object. So I set json inside the model Attribute mav.addObject("employeeJson", empJson) and parsed it using json.Parse() to convert it into Java Script Object and was able to use it with Angular expressions on the jsp Below are the steps which I followed:
1. Spring Controller
In your controller, set json in the model attribute. For e.g.
mav.addObject("employeeJson", empJson);
Make sure before adding it to ModelAttribute, replace quotes with html entity " otherwise there will be error while parsing the json
empJson.replaceAll("\"", "\&\quot;");
2. Jsp
Use angular init directive, data-ng-init="yourController.getEmployeeData('${employeeJson}')"
3. In Angular js parse this json to get the json
var vm = this;
vm.employee = {};
vm.getEmployeeData = function(employeeJson){,
vm.employee = JSON.parse(employeeJson);
};
4. Inside jspI can access employee name as
{{yourController.employee.firstName}}

What parameters should take a controller's method, which is deleting records from the database?

I'm creating a simple training project. I've implemented a controller method, which deletes an item from the list. The method is looking like this:
#Controller
#RequestMapping(value = "/topic")
public class TopicController {
#Autowired
private TopicService service;
...
#RequestMapping(value = "/deleteComment/{commentId}", method = RequestMethod.POST)
public String deleteComment(#PathVariable int commentId, BindingResult result, Model model){
Comment deletedComment = commentService.findCommentByID(commentId);
if (deletedComment != null) {
commentService.deleteComment(deletedComment);
}
return "refresh:";
}
}
This method is called from the button-tag, which is looking in the following way:
<form><button formaction = "../deleteComment/1" formmethod = "post">delete</button></form>
In my project the form-tag is looking like a clickable button. But there is a serious problem: controller's method is never triggered. How can I trigger it, using a button-tag?
P.S. the call is performed from the page with URI http://localhost:8080/simpleblog/topic/details/2 and controller's URI is the http://localhost:8080/simpleblog/topic/deleteComment/2
UPDATE:
I've created hyperlink 'delete', clicking on which should delete a comment, and I received an exception
java.lang.IllegalStateException: Errors/BindingResult argument declared without preceding model attribute. Check your handler method signature!
And it's true: I really have no #ModelAttribute in my controller method, preceding BindingResult parameter. But I have no clue, which type of type should it be?
<form>'s method attribute is GET by default. I don't know what are you trying to do with formmethod and formaction attributes, but in default HTML they mean nothing.
You must try something like:
<form action="../deleteComment/1" method="post">
<button>delete</button>
</form>
EDIT:
You are declaring some unused parameters in your method. BindingResult must be used with a #Valid annotated attribute (search #Valid here to see some examples), but this is not your case. So, please just try:
#RequestMapping(value = "/deleteComment/{commentId}", method = RequestMethod.POST)
public String deleteComment(#PathVariable int commentId){
...
}

Spring MVC 3 - How come #ResponseBody method renders a JSTLView?

I have mapped one of my method in one Controller to return JSON object by #ResponseBody.
#RequestMapping("/{module}/get/{docId}")
public #ResponseBody Map<String, ? extends Object> get(#PathVariable String module,
#PathVariable String docId) {
Criteria criteria = new Criteria("_id", docId);
return genericDAO.getUniqueEntity(module, true, criteria);
}
However, it redirects me to the JSTLView instead. Say, if the {module} is product and {docId} is 2, then in the console I found:
DispatcherServlet with name 'xxx' processing POST request for [/xxx/product/get/2]
Rendering view [org.springframework.web.servlet.view.JstlView: name 'product/get/2'; URL [/WEB-INF/views/jsp/product/get/2.jsp]] in DispatcherServlet with name 'xxx'
How can that be happened? In the same Controller, I have another method similar to this but it's running fine:
#RequestMapping("/{module}/list")
public #ResponseBody Map<String, ? extends Object> list(#PathVariable String module,
#RequestParam MultiValueMap<String, String> params,
#RequestParam(value = "page", required = false) Integer pageNumber,
#RequestParam(value = "rows", required = false) Integer recordPerPage) {
...
return genericDAO.list(module, criterias, orders, pageNumber, recordPerPage);
}
Above do returns correctly providing me a list of objects I required.
Anyone to help me solve the mystery?
If a controller method returns null, Spring interprets that as saying that you want the framework to render the "default view".
It would be better, I think, that when the method is #RequestBody-annotated, this logic should not apply, but perhaps that's hard to implement - how would it handle a null return from a method that normally returns XML, for example?
Anyway, to stop this from happening, you need to make sure you return something, like an empty Map.

Categories