Following is my servlet
<context:component-scan base-package="controllers" />
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
I have different controllers in controllers package. I want to set the route path in spring
like
when user enter
product/index
it should go to productControllers and index method of get/post type.
how to set the route mapping in spring framework.
Add a class level and method level #RequestMapping annotations as below
#Controller
#RequestMapping("/product")
public class ProductController{
#RequestMapping("/index")
public String index() {
return "welcome";
}
#RequestMapping("/getProducts")
public String getProducts() {
//your business logic
return "getProducts";
}
}
Then a request to http://localhost:8080/<context-root>/product/index in your local environment will return a welcome.jsp page.
Similarly http://localhost:8080/<context-root>/product/getProducts will return getProducts.jsp page.
If your have one more controller OrderController and a method getOrder in it, you can add the class level annotation #RequestMapping('/order') and method level annotation #RequestMapping('/getOrder') so that the URL http://localhost:8080/<context-root>/order/getOrder will invoke the getOrder controller method
You can use #RequestMapping("web/service") annotations on method you want to execute for that path:
#Controller
public class WelcomeService
{
#RequestMapping("/welcome")
public void welcomeMethod()
{
// do stuff
}
}
Related
I am using Rest Spring beans using xml Configuration.
I am trying to access variables which are initailized by beans using REST urls. But i am not able to fetch values. values fetched are null.
Is there anyway to initalize values and keep them intact and access them when i make call using urls.
Please suggest some way.
TIA
Edit:
Model:
#Repository
public class Topic{
private Integer id;
private String name;
//Getter and setter with constructor
}
Controller Class:
#RestController
#Singleton
public class TopicController{
#Autowired
private TopicService topicService;
public void setTopicService(TopicService topicService) {
this.topicService = topicService;
}
#RequestMapping("/topics")
public List<Topic> getAllTopics() {
System.out.println("in get all topics");
return topicService.getAllTopics();
}
}
ServiceClass:
#Service
public class TopicService {
#Autowired
private List<Topic> allTopics ;
public TopicService() {
}
public List<Topic> getAllTopics() {
return allTopics;
}
public void setAllTopics(List<Topic> allTopics) {
this.allTopics = allTopics;
}
}
Bean.xml
<bean name="topicService" id="topicService"
class="org.springtest.service.TopicService">
<property name="allTopics">
<list>
<bean class="org.springtest.model.Topic">
<property name="id" value="20" />
<property name="name" value="topic20" />
</bean>
<bean class="org.springtest.model.Topic">
<property name="id" value="30" />
<property name="name" value="Topic30" />
</bean>
</list>
</property>
</bean>
<bean id="topicController"
class="org.springtest.controller.TopicController"
scope="singleton">
<property name="topicService" ref="topicService"></property>
</bean>
output of
/localhost:8080/topics is:
{"id":null,"name":null}
Main class:
public static void main(String[] args) {
SpringApplication.run(CourseApiApp.class, args);
ApplicationContext context = new
ClassPathXmlApplicationContext("main/resources/Bean.xml");
TopicController tc= new TopicController();
System.out.println(tc.getAllTopics().size());// throwing nullpointerexception as topicService is null
}
I suggest you take a look at Jersey. It's a REST framework, one of the best in my opinion. Be sure to use a Snapshot of the last version of Jersey (I believe it's version 3), as it will have full support of Spring.
It's usage is simple.
A method controller will have 5 lines tops. It also encourages users to the best practices of a RESTful API. Such as defining the location header on a successful post, link headers referencing paging in a collection get, amongst others.
With Maven or Gradle in your project, integrating Jersey will take you 5 minutes.
I use it over Spring because it's sole purpose is implementing a REST API, while Spring has it simply as a feature.
I apologize for my lack of solution, just ask me if you need help getting started.
Andrés
That's because in the main method you have: TopicController tc= new TopicController(); which is wrong. The TopicController should be instantiated by Spring in your Main class using dependency injection. Above the main method you should write
#Autowired
private TopicController tc;, and remove the "tc" variable in the main method.
I try to create pdf view according this tutorial
I have controller:
#Controller
public class UserController {
#Autowired
UserDao userDao;
#RequestMapping(value = "/user_pdf", method = RequestMethod.GET, headers = "Accept=application/pdf")
public ModelAndView usersPdf(#ModelAttribute("model") ModelMap model) {
return new ModelAndView("pdfView", "listBooks", userDao.getAll());
}
}
I have servlet configuration:
<context:component-scan base-package="ua.epam.spring.hometask" />
<context:annotation-config/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<!--<property name="prefix" value="/WEB-INF/ftl"/>-->
<property name="suffix" value=".ftl"/>
</bean>
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl/"/>
</bean>
<bean id="PdfRevenueSummary"
class="ua.epam.spring.hometask.view.UserPdfView">
</bean>
And I have pdf builder:
#Component
public class UserPdfView extends AbstractPdfView {
protected void buildPdfDocument(Map model,
Document document, PdfWriter writer, HttpServletRequest req,
HttpServletResponse resp) throws Exception {
User user = (User) model.get("command");
Paragraph header = new Paragraph(new Chunk("Generate Pdf USing Spring Mvc",FontFactory.getFont(FontFactory.HELVETICA, 30)));
Paragraph by = new Paragraph(new Chunk("Author " + user.getFirstName() + " " + user.getLastName(),FontFactory.getFont(FontFactory.HELVETICA, 20)));
document.add(header);
document.add(by);
}
}
Surely I made appropriate <servlet-mapping> in web.xml
Questions:
How does binding work? From the tutorial I see that there is now linkage from Controller to PDF view
Please, help me to see how to fix my code. I see 404 error code now, and when I remove headers = "Accept=application/pdf" I see it tries to resolve it with FreeMarkerViewResolver
Accept header should be specified by the client (i.e. browser). #RequestMapping annotation should specify corresponding produces attribute to match the request. Basically, the annotation should look like this to match a request with Accept=application/pdf
#RequestMapping(value = "/user_pdf", method = RequestMethod.GET, produces = "application/pdf")
To test that method use something like postman specifying Accept header.
In order to get your pdf view resolved you want to configure a view resolver. In the tutorial it is this part (missing in your snippets):
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-pdf-views.xml</value>
</property>
</bean>
This bean declares that you have a /WEB-INF/spring-pdf-views.xml file that contains a configuration for beans responsible for views. However you can configure BeanNameViewResolver to avoid creating additional file (less flexible solution but choice is your):
<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
Take care to return the exact same view name in controller method as view-bean id (now they are inconsistent). Since
<bean id="PdfRevenueSummary" class="ua.epam.spring.hometask.view.UserPdfView"/>
you are supposed to return
return new ModelAndView("PdfRevenueSummary", "listBooks", userDao.getAll());
Another issue with your example is that probably the bean of UserPdfView class gets instantiated twice: it is declared in xml configuration and maybe picked up by component scan because of #Component annotation.
I have a class extending the AbstractExcelView class of Spring which renders an XML file. Within this class, I am injecting my Service bean for use. I am autowiring and component scanning my classes, and I would like to also do the same with this view class, but I am not clear how (or if it can be done). Here's what I'm trying to annotate from the config:
<bean id="export.xls" class="com.my.views.ReportExcelView">
<property name="url">
<value>/excel/template</value>
</property>
<property name="service" ref="testingService"/>
I am able to annotate the class with #Component, and the service with #Autowired, but I don't know of a strategy to annotate the URL. What I'd really like to do is condition it within the buildExcelWorkbook() call (based on something in the request), but it seems there is some initialization done before this, as I get an error trying to use my excel template with this method that indicates it does not have a handle to the Excel sheet. Any recommendations?
So your ReportExcelView probably looks like this right now. Make sure you use #Resource to wire a simple String.
package com.ex.springbasicex.view;
#Component
public class ReportExcelView{
#Resource(name="myUrl")
String url;
#Autowired
Service service;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Your context config probably should be like what is below using scanning. Below is how to set the myUrl String resource.
<context:component-scan base-package="com.ex.springbasicex.view" />
<bean id="myUrl" class="java.lang.String" >
<constructor-arg>
<value>/excel/template</value>
</constructor-arg>
</bean>
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I have case insensitive URLS in Spring MVC with annotated mappings
I have Controller having multiple #RequestMapping annotations in it.
#Controller
public class SignUpController {
#RequestMapping("signup")
public String showSignUp() throws Exception {
return "somejsp";
}
#RequestMapping("fullSignup")
public String showFullSignUp() throws Exception {
return "anotherjsp";
}
#RequestMapping("signup/createAccount")
public String createAccount() throws Exception {
return "anyjsp";
}
}
How can I map these #RequestMapping to case insensitive. i.e. if I use "/fullsignup" or "/fullSignup" I should get "anotherjsp". But this is not happening right now. Only "/fullSignup" is working fine.
I've tried extending RequestMappingHandlerMapping but no success. I've also tried AntPathMatcher like the guy mentioned there is another question on this forum but its also not working for #RequestMapping annotation.
Debugging console
Output console when server is up.
I've added two images which shows the problem. I've tried both the solutions mentioned below. The console says that it mapped lowercased URLS but when I request to access a method with lowercase url then it shows that the original map where the values are stored stilled contained MixCase URLS.
One of the approaches in How can I have case insensitive URLS in Spring MVC with annotated mappings works perfectly. I just tried it with combinations of #RequestMapping at the level of controller and request methods and it has worked cleanly, I am just reproducing it here for Spring 3.1.2:
The CaseInsensitivePathMatcher:
import java.util.Map;
import org.springframework.util.AntPathMatcher;
public class CaseInsensitivePathMatcher extends AntPathMatcher {
#Override
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
return super.doMatch(pattern.toLowerCase(), path.toLowerCase(), fullMatch, uriTemplateVariables);
}
}
Registering this path matcher with Spring MVC, remove the <mvc:annotation-driven/> annotation, and replace with the following, configure appropriately:
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayConverter"/>
<ref bean="jaxbConverter"/>
<ref bean="jsonConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean name="jaxbConverter" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean name="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
<bean name="caseInsensitivePathMatcher" class="org.bk.lmt.web.spring.CaseInsensitivePathMatcher"/>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="pathMatcher" ref="caseInsensitivePathMatcher"></property>
</bean>
Or even more easily and cleanly using #Configuration:
#Configuration
#ComponentScan(basePackages="org.bk.webtestuuid")
public class WebConfiguration extends WebMvcConfigurationSupport{
#Bean
public PathMatcher pathMatcher(){
return new CaseInsensitivePathMatcher();
}
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setPathMatcher(pathMatcher());
return handlerMapping;
}
}
The following simple solution should make #RequestMapping insensitive, whether it annotates a Controller or a method. Biju's solution should work too.
Create this custom HandlerMapping :
public CaseInsensitiveAnnotationHandlerMapping extends DefaultAnnotationHandlerMapping {
#Override
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
return super.lookupHandler(urlPath.toLowerCase(), request);
}
#Override
protected void registerHandler(String urlPath, Object handler)
throws BeansException, IllegalStateException {
super.registerHandler(urlPath.toLowerCase(), handler);
}
}
And add this in your [servlet-name]-servlet.xml :
<bean class="yourpackage.CaseInsensitiveAnnotationHandlerMapping" />
Note: if you don't want two HandlerMapping in your app, you may want to remove <mvc:annotation-driven /> (it instantiates a DefaultAnnotationHandlerMapping).
I have a Spring 2.x controller that extends SimpleFormController, which is deprecated as of Spring 3 in favor of annotated controllers. So I'm trying to convert it to use #Controller, with #InitBinder and #Valid for form validation. However, I can't find a way to use multiple validators with a Spring 3.x controller. How do I do this?
Here is what my controller's bean def currently looks like:
<bean name="/s/account" class="mywork.AccountSettingsController"
p:formView="forms/account"
p:successView="redirect:/app/s/account"
p:commandName="accountSettingsForm">
<property name="validators">
<list>
<ref bean="emailFormatValidator" />
<ref bean="uniqueEmailValidator" />
<ref bean="changeEmailValidator" />
<ref bean="passwordWithConfirmationValidator" />
<ref bean="changePasswordValidator" />
</list>
</property>
</bean>
It's the controller for a page which lets the user change their email address and password. The validator beans are legacy code, but I'm guessing they were split into separate classes for better reusability.
I'm trying to move all of that into the controller class itself, using annotations:
#Controller
#Secured({BaseController.ROLE_LOGGED_IN})
#RequestMapping("/s/account")
public class AccountSettingsController extends BaseController {
private static final String FORM_URL = "/forms/account";
private static final String FORM_NAME = "accountSettingsForm";
#InitBinder(FORM_NAME)
public void initBinder(WebDataBinder binder) {
// TODO: how to inject > 1 validator for the form?
binder.setValidator(...);
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView get() {
ChangePasswordOrEmailForm form = new ChangePasswordOrEmailForm();
...
return new ModelAndView(FORM_URL, FORM_NAME, form);
}
...
}
As far as I can tell, Spring 3 assumes a 1-to-1 relationship between: Controller-Form-WebDataBinder-Validator. I could create a composite validator that aggregates the 5 individual validator beans, and delegates the Validator#supports() and Validator#validate() calls to them, but is that really the best solution?
Another which i thought was to have a ValidatorFacade which in turn calls all the other validators one by one, that way you dont need to inject rather attach the ValidatorFacade with the initBinder and #Valid in front your form bean will automatically call the ValidatorFacade and everything taken care automatically. Just a thought.
This was an old problem.
Adding a comment here:
After spring 3.2.1.RELEASE, DataBinder#addValidators(Validator... validators) is available.
The best solution I can think of is to inject a collection of validators, and manually spin through them myself. So for now I removed initBinder() from my controller class, and here's what I added:
private List<Validator> accountSettingsValidators;
// Maps & Collections can't be #Autowired (by type OR name), so use the JSR 250 #Resource annotation to autowire by name
#Resource
public void setAccountSettingsValidators(List<Validator> accountSettingsValidators) {
this.accountSettingsValidators = accountSettingsValidators;
}
#RequestMapping(method = RequestMethod.POST)
protected ModelAndView processSubmit(HttpServletRequest request,
#ModelAttribute(FORM_NAME) ChangePasswordOrEmailForm form,
BindingResult bindingResult) {
for (Validator validator : this.accountSettingsValidators) {
ValidationUtils.invokeValidator(validator, form, bindingResult);
}
if (bindingResult.hasErrors()) {
return new ModelAndView(FORM_URL, FORM_NAME, form);
}
// process validated form
}
In my formControllers.xml Spring config, I create the list of validators to inject:
<util:list id="accountSettingsValidators">
<ref bean="emailFormatValidator" />
<ref bean="uniqueEmailValidator" />
<ref bean="changeEmailValidator" />
<ref bean="passwordWithConfirmationValidator" />
<ref bean="changePasswordValidator" />
</util:list>