What's a good way to structure spring web controllers when there are different web pages that share functionality?
Let's say there are a Tasks and a Task web page. I can start a task from both pages and I expect that I will remain on the same page when doing so .
What's the best way to do this? Am i forced to duplicate logic as follows:
#Controller
#RequestMapping("/tasks")
public class TasksController {
#GetMapping("/{id}/start")
public String start(#PathVariable String id) {
tasks.start(id);
return "redirect:/tasks.html";
}
}
and
#Controller
#RequestMapping("/task")
public class TaskController {
#GetMapping("/{id}/start")
public String start(#PathVariable String id) {
tasks.start(id);
return "redirect:/task.html";
}
}
ps. I'm not interested in async JavaScipt solutions.
You could use the Regex feature in path variables and get the page name in another variable. So, I would solve this the following way:
#Controller
public class TaskController {
...
#GetMapping({"/{page:tasks?}/{id}/start")
public String start(#PathVariable String page, #PathVariable String id) {
tasks.start(id);
return "redirect:/" + page + ".html";
}
}
If there's more logic or the entry points are quite different extract the common code to a service.
My thought: It is just a matter of how your design and name the controllers. Ideally, I create One controller per business domain object. instead of creating controllers for each page. If we name the Controller and Services for each business domain object, you could avoid this. So i would just have TaskController and call the same URI irrespective of where you call it either from Task page, TaskDetail page or TaskReport page.
Related
I am building a URL Shorter app (like Bitly). It is an SPA using Spring Boot & ReactJS. All web content is served off of index.html. All other routes are presumed to be shortLink redirect requests which should trigger a clickShortUrl() function to fetch the corresponding originalLink and redirect the user to that web address.
Therefore, I want the following routes to redirect to index.html:
#GetMapping(value = {"/", "/home", "/dashboard"})
public String redirect() {
return "forward:/index.html";
}
and all other/unknown routes to trigger a wildcard function:
#RequestMapping(value = "/{shortUrl}", method = RequestMethod.GET)
public Object clickShortUrl(#PathVariable("shortUrl") String shortUrl, #RequestBody ClickDTO request) {
// internalLogicHere
};
Individually, the mappings and functions are working. But combined, the /{shortUrl} wildcard route always takes precedence. I've googled around looking for ways to override this behavior. It seems to be possible a few ways, but all of my attempts have failed.
I read several posts like this suggesting to extend WebMvcConfigurerAdapter and override addViewControllers(ViewControllerRegistry registry) to define view controllers for specific routes. I don't really understand this. Is this the right path? If so, can someone help me understand what ViewControllerRegistry is all about and set me on the right path?
Thank you!
Answered my own question. Ended up using RegEx in the wildcard route to exclude the static paths used on the front end.
/** Redirect all '/' and '/dashboard/ requests to index.html. */
#GetMapping(value = {"path:/", "path:/dashboard"})
public String redirect() {
return "forward:/index.html";
}
and the fallback route:
/**
* Treat all routes as /{shortUrl} clicks except: '/', '/index.html, '/dashboard''
*/
#RequestMapping(value = "{_:^(?!index\\.html|dashboard).*$}")
public Object clickShortUrl(#PathVariable("shortUrl") String shortUrl, #RequestBody ClickDTO request) {
// internalLogicHere;
}
I am Planning to build a web application using Spring Boot as restful service.
my spring boot web restful application should be accessible by other application as well. In case if any one accessing the rest service from other application then my application should work as expected.
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
In above example if call is made from outside of application then the rest service should return JSON output.
One way we can have some variable to distinguish as request variable. But I do not want like that. Please share some standard way.
Appreciate your help.
Idiomatic way is to rely on Accept request header.
If requester presents Accept: application/json then return him JSON data (REST API).
If requester provides you with Accept: application/xhtml+xml return him HTML (web frontend).
Implementation-wise you should is to be done use #RequestMapping with consumes argument. You need two methods. If business logic for both paths is the same then in could be reused. Business logic should reside in another method or in separate #Service. Business logic on its own should not know, care or rely on transport protocol (HTTP), serialization of request response or presentation. Business logic should just work with POJOs and leave serialization to #Controller.
#Controller
#RequestMapping("/greeting")
public class GreetingController {
#RequestMapping(consumes="application/json")
#ResponseBody //required if you want to return POJO (spring will serialize it to response body)
public void rest() {
//return POJO, it will be serialized to JSON. or serialize pojo
directly and return response with manually set body and headers.
}
#RequestMapping(consumes="application/xhtml+xml")
public void html() {
//populate model, return string pointing to HTML to View
}
}
I suggest creating two controller classes, the second one using #RestController. Then, have two paths; the second could be "rs/greeting". This approach, which separates the Web and RESTful concerns, is much more extensible and doesn't require any weird headers that most clients don't want to deal with.
Before asking question first i describe the scenario. I have multiple servlet contexts. suppose /**book**/review url is corresponding to book-servlet.xml and /**report**/update corresponds to report-servlet.xml.
Here are two controllers
#Controller
#SessionAttributes(BookController.COMMAND_NAME)
public class BookController {
public static final String COMMAND_NAME = "login";
#GetMapping("/book/review")
public String show(ModelMap modelMap) {
modelMap.put(COMMAND_NAME, getBook());
return "redirects:" + "/report/update"; //it redirects to second controller method
}
}
#Controller
#SessionAttributes(ReportController.COMMAND_NAME)
public class ReportController {
public static final String COMMAND_NAME = "report";
#GetMapping("/report/update")
public String show(ModelMap modelMap) {
modelMap.put(COMMAND_NAME, getReport());
return "redirects:" + "/report/done";
}
}
Observe this two controller. When i put getBook() in model it stores this object in session after method excution since #SessionAttributes(BookController.COMMAND_NAME) is added above class definition. But, after redirection to /report/update (different servlet context) we are also trying to put getReport() in model. And after handler method excution spring should put the object in session as it does for first case.Unfortunately for second controller it doesn't store it in session.
My question is if the first controller method can successfully save it in session why the second controller can't? Or is it for servlet context switching / multiple get requests or something like this. Please i need to know the reason it behaves weirdly. I am now totally confused about #SessionAttributes. It should work same for both controllers.
After studying on this i found that it is wise to use RedirectAttributes for this type of redirect scenario. Cause ModelMap is intended for scenario where you use this ModelMap attributes to rendering a view. And FlashAttribute will survive after immediate redirection to another handler method and then be erased. So, i did my solution using RedirectAttrinute support from spring.
I'm assuming this is very easy to do, but I've been searching for a couple hours on Google and StackOverflow with no luck.
For starters, I have a jsp page using Dojo. I use Dojo's request module to make AJAX calls to Spring controllers for all sorts of things. The controllers call services, the services call DAOs, etc etc.
Originally, I had a properties file that contained several configuration settings for my site. These were injected into various Spring components using #Value. I wanted to make these dynamically configurable from my admin page, so I added a domain object called "SiteSettings" that holds the values from my properties file. When my webapp starts up, if it can't fine a SiteSettings object in persistence, it creates a new one, populates it with the settings from my properties file, and persists it. If one is already in persistence, it just uses it.
All of the site properties are used on the back-end except for one, a boolean called "authenticationRequired". I need to access this value in a javascript value on the front-end. This would be very easy to obtain via AJAX after the page loads, but I thought it's kind of silly to have to make an additional request, when I'm sure I should just be able to get the value from my SiteSettingsService (which uses the DAO to get the domain object).
Right now, I'm getting the value directly from the properties file:
<fmt:bundle basename="swtc">
<fmt:message key="swtc.authenticationRequired" var="authenticationRequired"/>
</fmt:bundle>
<script type="text/javascript">
window.authenticationRequired = <c:out value="${authenticationRequired}"/>;
</script>
How can I modify this get the value from my service/dao/domain object instead of directly from my properties file? Here are my controller and service files... nothing fancy:
Controller:
#RequestMapping(value = "/getSiteSettings", method = RequestMethod.GET)
#ResponseBody
public ModelMap getSiteSettings(ModelMap model) {
try {
SiteSettings siteSettings = siteSettingsService.getSiteSettings();
model.addAttribute("siteSettings", siteSettings);
model.addAttribute("success", true);
} catch (Exception ex) {
logger.error("There was an error getting the site settings data. ", ex);
model.addAttribute("exceptionMessage", ex.getLocalizedMessage());
model.addAttribute("success", false);
}
return model;
}
Service:
#Override
public SiteSettings getSiteSettings(){
List<SiteSettings> siteSettings = siteSettingsDao.findAll();
if (siteSettings != null && !siteSettings.isEmpty()){
return siteSettingsDao.findAll().get(0);
}
return null;
}
In the controller method that is responsible for rendering the overall jsp that requires the property, you should just be able to put the authenticationRequired attribute on the Model so it becomes available in the page for output into the Javascript. You'd need to autowire an instance of the SiteSettingsService into that controller:
#Controller
public class MainPageController { // or whatever it happens to be called
#Autowired
private SiteSettingsService siteSettingsService;
#RequestMapping(value="/mainPage", method=RequestMethod.GET)
public String displayMainPage(Model model) {
SiteSettings siteSettings = siteSettingsService.getSiteSettings();
// Make the property available to the view
model.addAttribute("authenticationRequired", siteSettings.isAuthenticationRequired());
return "mainPage";
}
}
I want the client and server application to talk to each other using REST services. I have been trying to design this using Spring MVC. I am looking for something like this:
Client does a POST rest service call saveEmployee(employeeDTO, companyDTO)
Server has a similar POST method in its controller saveEmployee(employeeDTO, companyDTO)
Can this be done using Spring MVC?
Yes, this can be done. Here's a simple example (with Spring annotations) of a RESTful Controller:
#Controller
#RequestMapping("/someresource")
public class SomeController
{
#Autowired SomeService someService;
#RequestMapping(value="/{id}", method=RequestMethod.GET)
public String getResource(Model model, #PathVariable Integer id)
{
//get resource via someService and return to view
}
#RequestMapping(method=RequestMethod.POST)
public String saveResource(Model model, SomeResource someREsource)
{
//store resource via someService and return to view
}
#RequestMapping(value="/{id}", method=RequestMethod.PUT)
public String modifyResource(Model model, #PathVariable Integer id, SomeResource someResource)
{
//update resource with given identifier and given data via someService and return to view
}
#RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public String deleteResource(Model model, #PathVariable Integer id)
{
//delete resource with given identifier via someService and return to view
}
}
Note that there are multiple ways of handling the incoming data from http-request (#RequestParam, #RequestBody, automatic mapping of post-parameters to a bean etc.). For longer and probably better explanations and tutorials, try googling for something like 'rest spring mvc' (without quotes).
Usually the clientside (browser) -stuff is done with JavaScript and AJAX, I'm a server-backend programmer and don't know lots about JavaScript, but there are lots of libraries available to help with it, for example see jQuery
See also:
REST in Spring 3 MVC
Yes, Rest is very easy to implement using spring MVC.
#RequestMapping(value="/saveEmploee.do",method = RequestMethod.POST)
#ResponseBody
public void saveEmployee(#RequestBody Class myclass){
//saving class.
//your class should be sent as JSON and will be deserialized by jackson
//bean which should be present in your Spring xml.
}