Following this guide I have configured a controller to handle the display and validation of a very simple form. I am using a Thymeleaf th:href to link to the form.
<a th:href="#{/event/create}">Create Event</a>
The problem is that both the showForm() and checkEventForm() are being invoked one after the other upon clicking the link. The result is a quick redirect away from the form back to / since checkEventForm() is also processed.
Are both GET and POST meant to be processed??
The Link :
<div id="sidebar">
<ol>
<li><a th:href="#{/}">Join Event</a></li>
<li><a th:href="#{/event/create}">Create Event</a></li>
</ol>
</div>
MvcConfig :
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
}
CreateEventController :
#Controller
public class CreateEventController {
#RequestMapping(value="/event/create", method=RequestMethod.GET)
public String showForm(CreateEvent event) {
return "createEvent";
}
#RequestMapping(value="/event/create", method=RequestMethod.POST)
public String checkEventForm(#Valid CreateEvent event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "createEvent";
}
return "redirect:/";
}
}
After some digging it turned out to be nothing with my program at all! It was in fact a chrome extension (lastpass) that had an auto-saved password for localhost which was attempting an automatic login and submitting the form! Talk about driving yourself nuts!
Related
I've either completely miss-understood converters, or all the examples/blogs/docs I've found are assuming I've done a step... My understanding is that when I post a String, the converter will pick it up and give the Controller a POJO instead.
The problem I have is the converter is never being called.
The payload coming into the controller is a standard JSON API string
{
"data":{
"attributes":{
"created-date":null,
},
"type":"steaks"
}
}
The converter:
#Slf4j
#Configuration
public class SteakConverter implements Converter<String, Steak> {
#Override
public Steak convert(String value) {
log.info("converter value {}", value);
return new Steak.builder().build();
}
}
The controller:
#Slf4j
#CrossOrigin
#RestController
public class SteakController {
#PostMapping("/steaks")
public ResponseEntity<String> create(#RequestBody Steak steak) {
}
}
From reading all the blogs and docs I can find this should be all that's needed. However I've also tried manually registering with the following:
#Slf4j
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
log.info("Converter registered");
registry.addConverter(new SteakConverter());
}
}
This is called during application startup.
The only thing I can think of is that the converter/controller doesn't think the payload is in fact a String, so is ignoring it?
How can I debug this, or just make it work?
Here is a working sample application that shows my problem.
Cheers
This question already has answers here:
Spring Boot with redirecting with single page angular2
(12 answers)
Closed 2 years ago.
I want to configure my Spring Boot app to redirect any 404 not found request to my single page app.
For example if I am calling localhost:8080/asdasd/asdasdasd/asdasd which is does not exist, it should redirect to localhost:8080/notFound.
The problem is that I have a single page react app and it runs in the root path localhost:8080/. So spring should redirect to localhost:8080/notFound and then forward to / (to keep route).
This is the full Spring Boot 2.0 example:
#Configuration
public class WebApplicationConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/notFound").setViewName("forward:/index.html");
}
#Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
return container -> {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
"/notFound"));
};
}
}
This should do the trick: Add an error page for 404 that routes to /notFound, and forward that to your SPA (assuming the entry is on /index.html):
#Configuration
public class WebApplicationConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/notFound").setViewName("forward:/index.html");
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
"/notFound"));
};
}
}
In case anyone stumbles here looking for how to handle Angular/React/other routes and paths in a Spring Boot app - but not always return index.html for any 404 - it can be done in a standard Spring controller RequestMapping. This can be done without adding view controllers and/or customizing the container error page.
The RequestMapping supports wild cards, so you can make it match against a set of well known paths (ie. angular routes etc.) in your application and only then return forward index.html:
#Controller
public class Html5PathsController {
#RequestMapping( method = {RequestMethod.OPTIONS, RequestMethod.GET}, path = {"/path1/**", "/path2/**", "/"} )
public String forwardAngularPaths() {
return "forward:/index.html";
}
}
Another option (borrowed from an old Spring article here: https://spring.io/blog/2015/05/13/modularizing-the-client-angular-js-and-spring-security-part-vii) is to use a naming convention:
#Controller
public class Html5PathsController {
#RequestMapping(value = "/{[path:[^\\.]*}")
public String redirect() {
return "forward:/index.html";
}
}
The above configuration will match all paths that do not contain a period and are not already mapped to another controller.
//add this controller : perfect solution(from jhipster)
#Controller
public class ClientForwardController {
#GetMapping(value = "/**/{path:[^\\.]*}")
public String forward() {
return "forward:/";
}
}
Here the security configuration (SecurityConfig.java)
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private UserSecurityService userSecurityService;
private BCryptPasswordEncoder passwordEncoder() {
return SecurityUtility.passwordEncoder();
}
private static final String[] PUBLIC_MATCHERS = {
"/css/**",
"/js/**",
"/data/**",
"/sound/**",
"/img/**",
"/",
"/login",
"/logout,
"/error",
"/index2",
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().
/* antMatchers("/**").*/
antMatchers(PUBLIC_MATCHERS).
permitAll().anyRequest().authenticated();
//.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login");
http
.csrf().disable().cors().disable()
.formLogin().failureUrl("/login?error")
.defaultSuccessUrl("/index2")
.loginPage("/login").permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/?logout").deleteCookies("remember-me").permitAll()
.and()
.rememberMe()
.and()
.sessionManagement().maximumSessions(3600)
.and().
invalidSessionUrl("/login");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
}
}
If not found any resource redirect to error page
#Controller
public class IndexController implements ErrorController{
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return PATH;
}
#Override
public String getErrorPath() {
return PATH;
}
}
Error page like
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1000/xhtml"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<meta http-equiv="refresh" content="5;url=/login" />
<body>
<h1>Page not found please login the system!</h1>
</body>
</html>
Simply implementing the org.springframework.boot.web.servlet.error.ErrorController did the trick for me. I use SpringBoot 2.0 with React. (If you are interested in how to do that here is a boilerplate project made by me: https://github.com/archangel1991/react-with-spring)
#Controller
public class CustomErrorController implements ErrorController {
#Override
public String getErrorPath() {
return "/error";
}
}
I am not sure why is this working though.
Getting started with Spring MVC, when I enter my page it loads the welcome.jsp as plain text(just shows source), I know people asked this 1 million times, But I looked over many of the questions and didn't find my solution as most of them using XML and I use java.. I am still not experienced enough to switch between the two.
conf.class :
public class conf extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/views/");
bean.setSuffix(".jsp");
return bean;
}
}
ServletInitializer.class:
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Demo4Application.class,conf.class);
}
}
welCont.class (Controller) :
#Controller
#RequestMapping("/spring/")
public class welCont {
#RequestMapping(method = RequestMethod.GET)
public String wel(ModelMap model)
{
model.addAttribute("test","testme");
return "welcome";
}
}
welcome.jsp :
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>${test}</h2>
</body>
</html>
I think wrong thing is
#RequestMapping("/spring/")
normally "/spring/" request should go to DispatcherServlet. Not sure in spring boot.
try with this.
#Controller
public class WelCont {
#RequestMapping(value="/foldername_if_avaiable/welcome/", method=RequestMethod.GET )
public ModelAndView wel(
#RequestParam(value="id", required=false) String id,
ModelMap model,
HttpSession session,
HttpServletRequest req) throws Exception {
model.addAttribute("test","testme");
return new ModelAndView("/foldername_if_avaiable/welcome", model);
}
}
reference
I have a Spring Boot (v1.3.6) single page application (angular2) and i want to forward all request to the index.html.
A request to http://localhost:8080/index.html is working (200 and i get the index.html) but http://localhost:8080/home is not (404).
Runner.class
#SpringBootApplication
#ComponentScan({"packagea.packageb"})
#EnableAutoConfiguration
public class Runner {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext run = SpringApplication.run(Runner.class, args);
}
}
WebAppConfig.class
#Configuration
#EnableScheduling
#EnableAsync
public class WebAppConfig extends WebMvcConfigurationSupport {
private static final int CACHE_PERIOD_ONE_YEAR = 31536000;
private static final int CACHE_PERIOD_NO_CACHE = 0;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.setOrder(-1);
registry.addResourceHandler("/styles.css").addResourceLocations("/styles.css").setCachePeriod(CACHE_PERIOD_ONE_YEAR);
registry.addResourceHandler("/app/third-party/**").addResourceLocations("/node_modules/").setCachePeriod(CACHE_PERIOD_ONE_YEAR);
registry.addResourceHandler("/app/**").addResourceLocations("/app/").setCachePeriod(CACHE_PERIOD_NO_CACHE);
registry.addResourceHandler("/systemjs.config.js").addResourceLocations("/systemjs.config.js").setCachePeriod(CACHE_PERIOD_NO_CACHE);
registry.addResourceHandler("/**").addResourceLocations("/index.html").setCachePeriod(CACHE_PERIOD_NO_CACHE);
}
}
styles.css, /app/third-party/xyz/xyz.js,.. are working (200 and i get the correct file). Only /** to index.html is not working.
You can also add a forwarding controller like:
#Controller
public class ForwardingController {
#RequestMapping("/{path:[^\\.]+}/**")
public String forward() {
return "forward:/";
}
}
The first part {path:[^\\.]+} matches one or more of any character other than .. This makes sure request for a file.ext doesn't get handled by this RequestMapping. If you need to support sub-paths to also be forwarded, put /** outside of the {...}.
This one didn't work for me:
return "forward:/";
Thanks to Spring MVC #RestController and redirect I found a nicely working solution:
#RequestMapping(value = "/{[path:[^\\.]*}")
public void redirect(HttpServletResponse response) throws IOException {
response.sendRedirect("/");
}
Without looking at logs I'm not entirely sure why its not being mapped correctly, however if you want to map URLs to a view (HTML) then you will probably be better off using the viewController mechanism spring provides http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-config-view-controller. e.g.
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
(taken from spring docs linked above - this is how you should map a url to a view rather than re-purposing the mapping for static resources.)
I'm not sure if there is any kind of suffix filtering for the resource mapping - e.g. I don't know how spring decides to map requests to the ResourceHttpRequestHandler - have you tried (just to confirm or deny) whether something like http://localhost:8080/home.html amps to anything?
It's also possible that the html mapping you have defined above is just being ignored and the index.html is just working because of Spring-Boot's default home page behaviour: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java#L108
I had the same problem and the following worked for me. My html files are inside src/main/resources/static/app
The key was to remove #EnableWebMvc and add "classpath:/static/app/" to addResourceLocations! Hope this helps.
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/","classpath:/static/app/", "classpath:/public/" };
#Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
// forward requests to /admin and /user to their index.html
registry.addViewController("/portal").setViewName(
"forward:/app/index.html");
}
};
}
Hi all I am getting the following error whenever I am trying to invoke validator in my spring
Servlet.service() for servlet spring threw exception: java.lang.IllegalStateException: Invalid target for Validator
Please have a look and help me out in this error, previously I user the validation for login page and it is working fine but now its not working.
Here is my code snippet .
Controller
#Controller
public class NewUserRegistration
{
#Autowired
private UserService userService;
#Autowired
private NewUserValidator newUserValidator;
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.setValidator(newUserValidator);
}
#RequestMapping(value="/newUserAdd", method=RequestMethod.POST)
public String addUser(#ModelAttribute("user")#Valid User user,BindingResult result, Model model)
{
return "NewUser";
}
}
Validator
#Component
public class NewUserValidator implements Validator
{
#Override
public boolean supports(Class<?> classz)
{
return NewUserRegistration.class.equals(classz);
}
#Override
public void validate(Object obj, Errors error)
{
//Validation login for fields
}
}
JSP Page
<form:form action="newUserAdd" method="POST" modelAttribute="user">
<center>
<table>
<tr><td>User Id:</td><td><input name="userId" type="text" /></td><td><font color="red"><c:out value="${userIdError}" /></font> </td></tr>
<tr><td>Password:</td><td><input name="userPassword" type="password"/></td><td><font color="red"><c:out value="${userPasswordError}" /></font></td></tr>
<tr><td>Confirm Password:</td><td><input name="userConfirmPassword" type="password"/></td><td><font color="red"><c:out value="${userPasswordError}" /></font></td></tr>
<tr><td>Name:</td><td><input name="userName" type="text"/></td><td><font color="red"><c:out value="${userPasswordError}" /></font></td></tr>
<tr><td></td><td><input type="submit" value="Create"/></td></tr>
</table>
</center>
</form:form>
The problem is actually in Validator class you are using NewUserRegistration's object which is wrong because you want to validate your User's object not your NewUserRegistration's object.
#Override
public boolean supports(Class<?> classz)
{
return NewUserRegistration.class.equals(classz);
}
which should be
#Override
public boolean supports(Class<?> classz)
{
return User.class.equals(classz);
}