Thymeleaf th:href invoking both a post and get - spring

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

Spring boot 2.0 custom converter being ignored

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

Configure spring boot to redirect 404 to a single page app [duplicate]

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.

Spring mvc jsp not rendering

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

Spring boot single page application - forward every request to index.html

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");
}
};
}

Invalid target for Validator in spring error?

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);
}

Resources