In my Spring MVC demo project I am using path variable annotation.
Controller looks like below.
#Controller
#RequestMapping(value = {"/", "/login"})
public class HelloWorldController {
#RequestMapping(method = RequestMethod.GET)
public String helloWorld(final Model model) {
model.addAttribute("message", "Hello World!");
System.out.println("hello world");
return "jsp/login.jsp";
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getLoginId(#PathVariable final int id, final Model model) {
model.addAttribute("message", "Hello World!");
System.out.println("hello world 2" + id);
return "jsp/login.jsp";
}
}
Now when I trying to access the below url
http://localhost:9080/ExampleSpring/login
the controller is going to login.jsp page. This is an expected result.
but when the url is changed to
http://localhost:9080/ExampleSpring/login/9
I am getting the below error and java.io.FileNotFoundException: JSPG0036E: Failed to find resource /login/jsp/login.jsp
Could anybody please tell me the reason behind this?
Actually I guess this could a reason and solution .. I guess for the former case ie. http://localhost:9080/ExampleSpring/login case it tries to look for the resource after the context root that is inside WEB-INF. But in the later case as it is ecountered a second / after /login it tries to find the resource inside WEB-INF/login folder which actually does not exists, thus the error came.
Soulution could be , to use "../jsp/login.jsp" which will force to check the resource from the context root. What ever long request string u can pass this will ensure that the resources are searched from the context root.
May be thats why to avoid this confusion spring has come up with View Resolver.
Please correct me if I am wrong.
Thanks all.
If I correctly understand you comments, you have no configured view resolver, so you simply forward to the view passed as view name.
As you have no initial / you fall in the hell of relative URLs. In that case, you should write :
return "/jsp/login.jsp";
if the view is at /WEB-INF/jsp/login.jsp
BTW, the reason for ViewResolvers is (among others such as easy refactoring) to do that low level work of adding /jsp/ as prefix and .jsp as suffix. So IMHO, you really should considere to add one to your config.
I think you should put :
....#PathVariable("id") int id ....
Related
I have written a book catalog in Spring.
It collects books (pdf, epub, mobi, ebook) from a directory, collects some metadata from them, stores them in a DB and then puts them in a List that is made available to my views:
#Slf4j
#Controller
public class BookCatalogController {
// == Fields ==
private final BookService bookService;
#Autowired
public BookCatalogController(BookService bookService){this.bookService = bookService; }
// == Model attributes ==
#ModelAttribute
public List<Book> bookData(){ return bookService.getBooksFromMemory(); }
public static final File bookDirectory= new File("D:\\edu_repo\\ebooks_test\\");
.
.
.
// Catalog Simple View
#GetMapping(Mappings.CATALOG_SIMPLE)
public String catalogSimple(Model model){
log.info("catalogSimple method called");
// This is adding the entire BookManager book list into the model.
model.addAttribute(AttributeNames.BOOK_DATA, bookData());
return ViewNames.CATALOG_SIMPLE;
}
// Catalog Detail View
#GetMapping(Mappings.CATALOG_DETAIL)
public String catalogDetail(Model model){
log.info("catalogDetail method called");
// This is adding the entire BookManager book list into the model.
model.addAttribute(AttributeNames.BOOK_DATA, bookData());
return ViewNames.CATALOG_DETAIL;
}
.
.
.
#GetMapping(Mappings.LOAD_BOOKS)
public void loadBooks(Model model) {
bookService.loadBooksFromDirectory(bookDirectory);
}
}
Obviously I'm not using #GetMapping(Mappings.LOAD_BOOKS) properly as you can see in the error below:
The error:
There was an unexpected error (type=Internal Server Error, status=500).
Error resolving template [load-books], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [load-books], template might not exist or might not be accessible by any of the configured Template Resolvers
How does one invoke a method like I am doing but without Spring trying to redirect the user to another view?
I'm not expecting the page to update at all since I'm not returning a View!
When you click a link in your browser with a load-books anchor, your browser sends it to the server and waits for result, which causes your page to be reloaded. Once the request to a load-books endpoint reached to the server, Spring MVC handles this and starting to looking up an appropriate controller with its method. It founds public void loadBooks(Model model) in your case. When Spring MVC invokes the method, it expects to obtain a view name to resolve and return back to your browser.
Since you haven't provided a View or String as a return type, Spring MVC used the endpoint's path as a view name (I'm not seeing your Mappings.LOAD_BOOKS constant, but it supposed to be load-books).
If you're not going to return any view back to the browser, you can annotate the method like that:
#GetMapping(Mappings.LOAD_BOOKS)
#ResponseBody
public void loadBooks(Model model) {
which tells Spring to treat void as a response body.
But it's not preventing a page refreshing, you'll just see an empty page after clicking the link. In order to fix this you can redirect a user to another page by returning the following string (without ResponseBody annotation on the method)
return "redirect:/path-to-redirect";
When Spring MVC sees this prefix it redirects you to another controller, but user going to notice that too.
If you really don't want to see a blank page for a moment, you'll have to use some JavaScript to perform AJAX request to the server when button is clicked.
Actually, it seems that you want to preload some files in a service by a given path. If it's all you want to do, you can use Spring's runners like that:
#Component
class Preloader implements ApplicationRunner {
private final BookCatalogService bookService;
#Autowired
public Preloader(BookCatalogService service) {
this.bookService = service;
}
#Override
public void run(ApplicationArguments args) throws Exception {
bookService.loadBooksFromDirectory(BookCatalogController.bookDirectory);
}
}
Spring automatically calls all registered runners when application is ready, so your code will be executed without having a user to visit load-books endpoint.
I have a Spring Boot app that includes some controllers and static resources. I need to be able to have a controller that matches:
/hello
and
/hello/
but not
/wonder/hello
(or anything else). It seems that when I use the following mapping:
#RequestMapping(value = "/{slug}", method = RequestMethod.GET)
public String mapping(#PathVariable("slug") String slug)
it does a "catch-all" whereas I only need it to catch the first level. This causes issues with the static resource mapping.
use #RequestMapping("/hello") in the starting of controller
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.
We're having an issue where the values that we take in via #PathVariable or via binding under moderate load are getting assigned to the wrong variables.
For doing these tests we're always hitting the same URL's. Hitting the URL's slowly produce the correct results every time.
We're using Spring 3.0.5.RELEASE but have tired up to 3.1.2.RELEASE and still have the issue. We can't see anything else that's changed lately and we've had these services in production for over a year now and they seem to have just recently started throwing these.
Code Example for Binding:
#RequestMapping(value = "config/selectItem")
public ModelAndView selectItem(#Valid selectItemCommand command, BindingResult bindResults) {
if (!"test".equals(command.getName())
{
//Fails into here
int i =0;
}
}
Code Example for Path Variables:
#RequestMapping(value = "config/selectItem/{name}",method = RequestMethod.GET)
public ModelAndView selectItem(#PathVariable("name") String name)
{
if (!"test".equals(name))
{
//Fails into here
int i = 0;
}
}
I added a HttpServletRequest request to the parameter list and the URL that it shows has the correct values.
I totally forgot but we had this come up about a year ago and I had put in a ticket at the time that I thought had resolved it but has not.
Spring MVC is dropping a #PathVariable
It turns out that we implemented a StringTrimmerEditor incorrectly and it wasn't threadsafe.
At the moment I am bussy with implementing a new url structure for our webshop. The new url structure should be more optimized for search engines. We also want that our old structure will still be working and will use a 301 to redirect to a the new structure.
The problem is: the new structure sometimes conflicts with the old urls.
Example of the old url mapping:
#RequestMapping(value = "/brand/{categoryCode}/{categoryName}/{brandGroup}.do", method = RequestMethod.GET)
New structure:
#RequestMapping(value = "/brand/{brandGroup}/{superCategoryName}/{categoryName}.do", method = RequestMethod.GET)
As you can see the url's have the same amount of values, so the old mapping will catch the new one and vice versa.
What is the best way to fix this? Using a url filter to rewrite the old ones to the new url structure?
You could use an URL router in Spring MVC; you can define conflicting routes within your app and handle them with route prorities (first route to match the request wins) and refine request matching.
Your routes configuration file could look like:
GET /brand/{<[0-9]+>categoryCode}/{categoryName}/{brandGroup}.do oldcontroller.oldAction
GET /brand/{<[a-zA-Z]+>brandGroup}/{superCategoryName}/{categoryName}.do newController.newAction
In spring boot, regular expressions can be used when mapping the #PathVariable, and this can be useful to resolve url conflicts:
#RestController
public class TestController {
#PutMapping("/test/{id:^[1-9][0-9]*}") // id must be a number greater that 1
public void method1(#PathVariable long id, #RequestBody DataDto1 data) {
}
#PutMapping("/test/foo")
public void method1(#Valid #RequestBody DataDto2 data) {
}
}