How to reference/build the URL of a (named) Spring Web #RequestMapping on a HTML page using Thymeleaf? - spring

I just started using Spring Web & Thymeleaf for the development of a simple webapplication.
It is going pretty well but so far I am unable to figure out how to prevent duplicate code for the hrefs and various paths/routes.
Example Spring get mapping:
...
#GetMapping(path="/hello")
public String hello(){
return "hello";
}
...
First HTML page (index.html) containing href to '/hello', using Thymeleaf:
...
<a th:href="#{/hello}">Hello</a>
...
Second HTML page (home.html) containing href to '/hello', using Thymeleaf:
...
<a th:href="#{/hello}">Hello</a>
...
How can I arrange my code so that I don't have to manual update the href on each HTML page when I change the path of a #GetMapping?
Basically I would like to assign the path '/hello' to a variable which i can then reference at each href and getmapping
BR, Kazi
I have been googling for a couple of hours but am unable to find any usable information on my problem.
I have noticed the 'name' attribute of the Spring '#GetMapping' annotation but have no idea if this provides a solution or how it should work together with Thymeleaf.
Also i know that the Django framework for Python based webapplications provides the ability to name routes (using urlpatterns) and reference these from within its templating language. I would expect a similar solution is available for the Spring framework.

I manage to find two solutions:
Solution 1
Using the capital letters of the controller name in combination with the name of the requestmethod, as described in section 11.1 'Building URIs to controllers' of the official Thymeleaf document Thymeleaf Tutorial: Thymeleaf + Spring. The capital letters of the controller name and the requestmethod name are separate by a '#'.
Important: The 'name' attribute of the #RequestMapping must not be used else Thymeleaf won't be able to resolve the view!
Note: Possibly this solution is preferred in case path variables or request parameters must be added to the url. (I have not investigated this.)
Example Controller
public class ExampleController {
#RequestMapping("/data")
public String getData(Model model) {
...
return "template"
}
#RequestMapping("/data")
public String getDataParam(#RequestParam String type) {
...
return "template"
}
}
Example links
<a th:href="${(#mvc.url('EC#getData')).build()}">Get Data Param</a>
<a th:href="${(#mvc.url('EC#getDataParam').arg(0,'internal')).build()}">Get
Data Param</a>
Solution 2
Using the 'name' attribute of the #RequestMapping. As discribe in the following answer to Stackoverflow question Url building by #RequestMapping name not working as expected with custom names
The first example only uses the 'name' attribute in the #RequestMapping at the method level.
Example Controller 1
#Controller
public class ExampleController {
#RequestMapping(value="/", method=RequestMethod.GET, name="home")
public String index(Model model) {
return "index"
}
}
Example link 1
<a th:href="${(#mvc.url('home')).build()}">Home</a>
The second example uses the 'name' attribute in the #RequestMapping at the method level and type level.
Example Controller 2
#Controller
#RequestMapping(value="/information", name="info")
public class ExampleController {
#RequestMapping(value="/organisation", method=RequestMethod.GET,
name="org")
public String contact(Model model) {
return "organisation"
}
}
Example link 2
<a th:href="${(#mvc.url('info#org')).build()}">Contact</a>

Related

Only one GET method in controller and yet getting "Not supported by Swagger 2.0: Multiple operations with path"

I know there are many questions with the same title but this one is different.
I was getting this error on my product controller, so to investigate the problem I created a demo controller in the ASP.NET Web API 2.
DemoController.cs
namespace WMWebAPIControllers.Controllers
{
[RoutePrefix("api/Demo")]
public class DemoController : ControllerBase
{
[HttpGet]
[Route("")]
public async Task<IHttpActionResult> GetProducts(int CatalogType, string ProductNo)
{
return Ok();
}
}}
The strange thing is demo controller have only one method. There is no method with which swagger would find ambuigity.
I don't understand the problem.
Below is the swagger error.
500 : {"message":"An error has occurred.","exceptionMessage":"Not supported by Swagger 2.0: Multiple operations with path 'api/Demo' and method 'GET'. See the config setting - \"ResolveConflictingActions\" for a potential workaround","exceptionType":"System.NotSupportedException"
The problem was I had one public method without route attribute. So swagger found ambiguity with the GetProducts() method which was configured for the empty route.
Marked that public method as [NonAction] attribute and issue solved.
It can also be marked private/protected to solve this problem but in my case, it was an interface method.
Hope this will help someone.

"Circular view path" with simple spring-boot app using #PathVariable

I have a simple spring-boot application that serves up static content. I have an index.html page and some js/css in /src/main/resources/public. I have a single simple controller as follows:
#Controller
public class PublicController {
#RequestMapping(value="/", method=RequestMethod.GET)
public String index() {
return "index";
}
}
That works as expected. I run curl http://localhost:8080/, and it delivers my html.
Now I want to modify the controller to take a path variable:
// ...
#RequestMapping(value="/{word}", method=RequestMapping.GET)
public String index(#PathVariable("word") String word) {
return "index";
}
But now I get a 500 with a big long "Circular view path" exception.
I presume what's happening is the view resolver is seeing that my controller can handle "/index", and realises that's not going to end well.
Is it possible to tell the view resolver to give the static resources priority?
When you use Thymeleaf and declare declare a ThymeleafViewResolver and a ServletContextTemplateResolver with a specific prefix and suffix, it builds the View differently, giving it a path like
WEB-INF/static/index.html
ThymeleafView instances locate the file relative to the ServletContext path by using a ServletContextResourceResolver
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);
which eventually
return servletContext.getResourceAsStream(resourceName);
This gets a resource that is relative to the ServletContext path. It can then use the TemplateEngine to generate the HTML. There's no way an endless loop can happen here.

What is the best way to pass values from jsp to controller in Spring mvc

I have a jsp page that is dynamic configured. By different choice, the page has different components, just like dropdown list, list box and datetime etc. For one choice, the page may have only two dropdown list, and by another choice, the page has more enter field. Each enter field has different name. Even one page has two dropdown lists, the two lists have different names.
Just want to get your suggestion, what is the best way to get all these values to controller in Spring MVC.
I would do it as follows:
JSP: enclose all the input fields (textbox/select/checkbox/radio) in <FORM> tags and set the action attribute of the form to call the Controller.
Controller:
Define a simple Java bean class with attributes corresponding to each of the input field in the form
Accept this class type as a parameter to the method in the controller that will be called upon <FORM> submission and annotate the parameter with #RequestBody.
See Mapping the response body with the #ResponseBody annotation for more details on completing the wiring.
Create a DTO to hold all possible form element
Class MyFormData {
private String name;
private Double salary;
private List<String> hobbies;
......
}
In controller, use #ResponseBody
#RequestMapping(value = "/something", method = RequestMethod.POST)
public void handle(#RequestBody MyFormData formData, Model model) {
...
}
If your UI is so dynamic, you might as well bite the bullet and go for a Javascript solution. Spring MVC will simply receive and return JSON as #RequestBody and #ResponseBody and not worry so much about the fundamental MVC.
JSP can only get you so far in terms of dynamic UI behavior. Once you get comfortable with JQuery and/or AngularJS, you'll never look back.

read Asp.Net Web api GET values from url

I am trying to map /{Controller}/{Variable1}/{Variable2}/{Variable3} to a GET method in controller
public TestController{
public ActionResult Get([FromUrl] Entity instance){}
}
So I need to map variables to the entity.
To put it into an example
/Product/{category}/{filter1}/{filter2}/
Entity
public class ProductSearchRequest
{
public string Category{get;set;}
public string filter1 {get;set;}
public string filter2 {get;set;}
}
Controller
public ProductController: Controller {
public ActionResult GET([FromUri] ProductSearchRequest productSearchRequest){
}
}
[EDITED]
Had to do following changes to get this working
Instead of RouteCollection.MapHttpRoute use HttpConfiguration.Routes.MapHttpRoute as this is API routing not MVC routing.
Inherit controller from ApiController rather than Controller which I was before.
Basically you are not going to be able to do that. Complex types are not compatible with the routing mechanism.
Take a read of this article. But this paragraph explains why the routing mechanism cannot do what you are asking.
A complex type can only bind to the URI through a custom binding. But
in that case, the framework cannot know in advance whether the
parameter would bind to a particular URI. To find out, it would need
to invoke the binding. The goal of the selection algorithm is to
select an action from the static description, before invoking any
bindings. Therefore, complex types are excluded from the matching
algorithm.
Therefore the basic rule is:
For every parameter of the action, if the parameter is taken from the
URI, then the parameter name must be found either in the route
dictionary or in the URI query string. (Optional parameters and
parameters with complex types are excluded.)
Which means you need to define your action like so:
public ActionResult GET(string Category, string filter1, string filter2){
}
And your route template:
/{controller}/{category}/{filter1}/{filter2}/

Spring+JSP url building best practices

I wonder if there are any good practices for addressing Spring controllers in JSP.
Suppose I have controller:
#Controller
class FooController {
// Don't bother about semantic of this query right now
#RequestMapping("/search/{applicationId}")
public String handleSearch(#PathVariable String applicationId) {
[...]
}
}
Of course in JSP I can write:
<c:url value="/search/${application.id}" />
But it's very hard to change url then. If you familiar with Rails/Grails then you now how this problem resolved:
redirect_to(:controller => 'foo', :action = 'search')
But in Spring there is so much UrlMappers. Each UrlMapper have own semantic and binding scheme. Rails alike scheme simply doesn't work (unless you implement it yourself). And my question is: are there any more convenient ways to address controller from JSP in Spring?
I hope i understood your question. I think your asking about how to maintain urls when url strings are in jsp and controller mappings.
Your Controller should do the logic, your JSP should do the output. Constructing an Url should be the responsability of the controller handling it. So
class SearchController {
#RequestMapping("/search/{applicationId}")
public String handleSearch(#PathVariable String applicationId) {
[...]
}
public String getUrl(Long applicationId) {
return "/search/" + applicationId;
}
}
class StartController {
private SearchController controller;
#ModelAttribute("searchUrl")
public String getSearchUrl() {
return fooController.getUrl(applicationId);
}
}
and in your start.jsp do
<c:url value="${searchUrl}" />
Try using Apache as a front end to remap the URLs:
http://www.simonecarletti.com/blog/2009/01/apache-rewriterule-and-query-string/
This way you can change the applicationId parameter from a query string into a friendly URL.
Additionally, here is the documentation for mod_rewrite:
http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html

Resources