How to dynamically create a pathvariable to create a view? - spring

How to dynamically create a pathvariable to create a view?
When the button is clicked, it moves to the corresponding url, such as localhost:8080/viewer/{id}.
I wonder how to pass the id to admin.html without creating a controller.
#Override
public void addViewControllers(ViewControllerRegistry registry) {
this.getPathView().forEach((path, view) -> registry.addViewController(path).setViewName(view));
}
As above, only the url to the current viewer exists because of dynamic url mapping.
When I pass the id in the script, I want to receive the id in admin.html and map it dynamically.

Related

Spring sending user to a view when no view is being requested

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.

Web Api Action Attribute not executing when request sent from a new tab

I have an Action Attribute
ReportAccessAttribute
Added this attribute to a controller action
ReportingController
My application is in angularjs
When tested with other controller and action this attribute works fine.
Now I am requesting that Action method of controller from my application by setting the url in an anchor tag.
This anchor tag opens a new tab and sends the request to server.
In this case Action attribute do not execute.
Please suggest.
Code Sample
Action Attribute
public class ReportAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
//Some work...!!
base.OnActionExecuting(actionContext);
}
}
Controller
public class ReportingController : Controller
{
public ReportingController()
{
//Some initializations..!!
}
[Filters.ReportAccess]
public ActionResult Report(string reportName)
{
//Report generation logic.
}
}
When accessed from my angularjs app it gets executed eg: using a service to call that method.
But when I create a url and set anchor tag value to that url and click on that anchor tag. It opens that url in new tab and then it don't executes.
Thanks for the response.
The problem was I create the attribute using namespaces from WebApi dll and the controller from MVC dll.
That's why it wasn't intercepted in the execution pipeline.

Populate data in spring model

I have kept a map in the page model. While invoking the page, am fetching few items from database and will keep that in the map so that i will use it in the JSP. Mainly these items are used for populating options in dropdown.
#RequestMapping(value = "/initSystemPage")
public String initSystemPage(#ModelAttribute("systemModel") SystemModel systemModel, ModelMap modelMap) {
Map<String, List<DropdownVO>> data = ** fetch items from database **;
systemModel.setData(data);
return "system";
}
Upon invoking the screen, i can get the items from the model and populate the values in the dropdown. So far fine. but the issue happens if i do any action like submitting the form. As am not keep an element in the JSP corresponding to the data attribute, upon submitting the form dropdown data is not mapped to model hence it is not available in the JSP after page refresh.
I dont want to populate the items in model in every action methods. if i keep the data in session attributes, is it possible to do the populate in a common method that need to be invoked in all actions? something like init-binder
If you want to keep your current design you can make use of #SessionAttributes annotation at class level to ensure that your systemModel attribute is stored in session and is accessible for subsequent requests. However make sure that you clear the attribute when you have completed form processing using SessionStatus. For example
#Controller
#SessionAttributes("systemModel")
public SystemPageController{
#RequestMapping(value = "/initSystemPage" , method = RequestMethod.GET)
public String initSystemPage(#ModelAttribute("systemModel") SystemModel systemModel, ModelMap modelMap) {
Map<String, List<DropdownVO>> data = ** fetch items from database **;
systemModel.setData(data);
return "system";
}
}
Alternatively you can use #ModelAttribute annotation to indicate methods which should be called for every request for the Controller to add model data.
#ModelAttribute("systemModel")
public SystemModel populateSystemModel(){
//code to populate and return
}
The signature is very flexible. You can include most parameters used with #RequestMapping methods such as HttpServletRequest #RequestParam , #PathVaribale etc

Duplicate RequestMapping

I'm working on an open source named OpenMRS supporting Spring MVC. I cannot modify core source for update purpose later. So I must write a module, something like plugin to add functions to the system. The problem is that I want to alter the original screen to mine by using portlet to redirect to my jsp. The controller of the core code is something like this:
#RequestMapping("/patientDashboard.form")
protected String renderDashboard(#RequestParam(required = true, value = "patientId") Integer patientId, ModelMap map){
....
return "patientDashboardForm";
}
I'm not familiar with Spring but as I know that when the url ends with /patientDashboard.form?patientId=xxx the function will call patientDashboardForm.jsp. Now I want to return to my jsp so I must define a new class with same code but return to my jsp (to do this because cannot modify the core code). But by defining same mapping /patientDashboard.form causes error "Cannot map handler XXX to URL path /patientDashboard.form: There is already handler YYY mapped".
So is there anyway to overcome this situation ?
There is no way to overrule an existing #RequestMapping. Each mapping must be unique. What you could do is the following. Instead of adding a request parameter, add a path parameter like this
#RequestMapping("/patientDashboard.form/{patientId}", method = RequestMethod.GET)
public String renderDashboard(#PathVariable("patientId") final long id, Model model) {
/* your code here */
}
This will create a new #RequestMapping that will differ from the existing one.
You have to create another #Controller extending the existing one. Then, you can define your custom mappings (you can't reuse the existing one) and reimplement the superclass methods at your convenience, redirecting to your view and defining custom logic there.
Example:
#Controller
#RequestMapping("/your_new_mapping")
public class YourController extends BaseController {
#Override
#RequestMapping("/patientDashboard.form")
public void renderDashboard(#RequestParam(required = true, value = "patientId") Integer patientId, ModelMap map){
// Call to default functionallity
super.renderDashboard(patientId, map);
...
// your custom code here
return "yourCustomJSPHere";
}
}

why setDisallowedFields for id? -- Spring petclinic example

From the Spring API, i understood that #InitBinder is used to initialize some binding rules..
In the petclinic example why we have setdisallowed("id")? when the id is not displayed on the form?
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
The id field is not displayed on the web page then why we are using the above code?
can we have some thing like this:
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("FirstName");
}
as per the above code the first name field of the owner object will not be set though user enters on the form? Is that correct?
link for the source
Because it can still be submitted if the end-user modifies the page or the request (for example using FireBug). Thus he can inject values into your bound object even if you don't want this.

Resources