I'd like to have my login form (for example) on multiple pages, and I want to pre-populate it with the current user's login (assuming that user is recognized via cookie). But I don't want every controller method for every possible page to have to provide a LoginForm bean for the form. I do want all the validation magic when the form is submitted, and then of course I want the result of the form to the same page the user was on when they submitted it.
I can't quite figure out how to accomplish this right now. Is it even possible?
EDIT:
I've got a Thymeleaf form like this:
<form action="#" data-th-action="#{/users/login}" data-th-object="${loginForm}" method="post">
<input type="text" placeholder="Email or Username" data-th-field="${loginForm.login}">
<input type="password" placeholder="Password" data-th-field="${loginForm.password}">
<button type="submit" name="login">Sign in</button>
<button type="submit" name="register">Register</button>
</form>
If I don’t create a LoginForm (my class) bean and stick it in the model under loginForm, then I get an exception on GET, when rendering the page.
You don't need to pass the LoginForm to multiple controllers, rather, you can centralize your code at a single place with a http filter by validating the Login form for the required urls as below:
LoginFormValidationFilter class:
#Component
public class LoginFormValidationFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURL();
if(url.equals(YOUR_LOGIN_URL)) {
//validate the request here for the required urls
//If request is invalid, send an error message back
}
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Web.xml filter mapping:
<filter>
<filter-name>LoginFormValidationFilter </filter-name>
<filter-class>xyz.LoginFormValidationFilter </filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFormValidationFilter </filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Related
How can I display an error message in the very same JSP when a user submits a wrong input? I do not intend to throw an exception and show an error page.
Easiest would be to have placeholders for the validation error messages in your JSP.
The JSP /WEB-INF/foo.jsp:
<form action="${pageContext.request.contextPath}/foo" method="post">
<label for="foo">Foo</label>
<input id="foo" name="foo" value="${fn:escapeXml(param.foo)}">
<span class="error">${messages.foo}</span>
<br />
<label for="bar">Bar</label>
<input id="bar" name="bar" value="${fn:escapeXml(param.bar)}">
<span class="error">${messages.bar}</span>
<br />
...
<input type="submit">
<span class="success">${messages.success}</span>
</form>
In the servlet where you submit the form to, you can use a Map<String, String> to get hold of the messages which are to be displayed in JSP.
The Servlet #WebServlet("foo"):
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/foo.jsp").forward(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String> messages = new HashMap<String, String>();
request.setAttribute("messages", messages); // Now it's available by ${messages}
String foo = request.getParameter("foo");
if (foo == null || foo.trim().isEmpty()) {
messages.put("foo", "Please enter foo");
} else if (!foo.matches("\\p{Alnum}+")) {
messages.put("foo", "Please enter alphanumeric characters only");
}
String bar = request.getParameter("bar");
if (bar == null || bar.trim().isEmpty()) {
messages.put("bar", "Please enter bar");
} else if (!bar.matches("\\d+")) {
messages.put("bar", "Please enter digits only");
}
// ...
if (messages.isEmpty()) {
messages.put("success", "Form successfully submitted!");
}
request.getRequestDispatcher("/WEB-INF/foo.jsp").forward(request, response);
}
In case you create more JSP pages and servlets doing less or more the same, and start to notice yourself that this is after all a lot of repeated boilerplate code, then consider using a MVC framework instead.
See also:
Our Servlets wiki page
What is the best practice for validating parameters in JSP?
I see tag "form-validation", so maybe you should just use JavaScript and client form validation? If you need validation with JSP, handle form data, and redisplay the form with an error message or accept form data, if it's correct.
I'm not quite sure what you mean by "display error message". If you have a standard error handling, then you can always check for options:
<%
if(wrong option selected)
throw new Exception("Invalid option selected");
%>
Of course, this is just the notion; preferably, you'd have your own exception class. But then again, I'm not quite sure what you're after.
I am trying to build a sample spring portlet, with LR 6.2 GA1.
Below is the source for the same https://docs.google.com/file/d/0By1kU5o_jlrublhUNXIxQ24wODQ/edit
On the ajax the parameters are not being fetched.The parameters always remain blank.
#Controller(value = "ProjectSearch")
#RequestMapping("VIEW")
public class ProjectSearch {
Log log_ = LogFactoryUtil.getLog(ProjectSearch.class);
#RenderMapping
public String handleRenderRequest(final RenderRequest request,
final RenderResponse response, Model model) {
System.out.println("ProjectSearch.handleRenderRequest()");
return "search_form";
}
#ResourceMapping("getProjectNameSuggestion")
public void getNameSuggestion(ResourceRequest request,
ResourceResponse response) throws IOException {
Map<String, String[]> map = request.getParameterMap();
for (Map.Entry<String, String[]> element : map.entrySet()) {
log_.info(element.getKey());
}
String entityName = ParamUtil.getString(request, "query");
log_.info("Entity name==>" + entityName);
}
}
#RenderMapping
public String handleRenderRequest(final RenderRequest request,
final RenderResponse response, Model model) {
System.out.println("ProjectSearch.handleRenderRequest()");
return "search_form";
}
#ResourceMapping("getProjectNameSuggestion")
public void getNameSuggestion(ResourceRequest request,
ResourceResponse response) throws IOException {
Map<String, String[]> map = request.getParameterMap();
for (Map.Entry<String, String[]> element : map.entrySet()) {
log_.info(element.getKey());
}
String entityName = ParamUtil.getString(request, "query");
log_.info("Entity name==>" + entityName);
}
}
Output-->05:23:24,148 INFO [http-bio-8080-exec-119][ProjectSearch:41] Entity name==>
Could any body tell me what is that I am doing wrong??
Solution:
Configure Requires Name Spaced Parameters to false in liferay-portlet.xml
Now need to do require Name spaced parameters to false then only form data is mapped in Action Request and Render Request. And also form data will be binding to model object or command object.
The following is configuration we need to do in liferay-portlet.xml file
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Required Name Space Parameter Behavior in Liferay
Liferay 6.2 we have to append portlet Name space for every name of input element i.e. form input elements or request parameters names otherwise portlet action class ignore the parameters which does not have portlet name space to names.
Scenario
Jsp page
In the following form we are not appending portlet name space to form input element names.
<portlet:actionURL var="addEmployeeActionURL" name="addEmployee">
<portlet:param name="<%=ActionRequest.ACTION_NAME%>" value="addEmployee"/>
</portlet:actionURL>
<form action="<%=addEmployeeActionURL%>" name="emplyeeForm" method="POST">
Employee Name<br/>
<input type="text" name="employeeName" id="employeeName"/><br/>
Employee Address<br/>
<input type="text" name="employeeAddress" id="employeeName"/><br/>
<input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/>
</form>
Portlet Class Action Method
public class EmplyeePortletAction extends MVCPortlet {
public void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException {
String employeeName=ParamUtil.getString(actionRequest,"employeeName");
String employeeAddress=ParamUtil.getString(actionRequest,"employeeAddress");
}
}
In above case employeeName and employeeAddress form input data not accessible in portlet class action .The form elements name are not appended with portlet name space such scenarios portlet class ignore those request parameters or form inputs
Solution:1
Need to append tag to every input element name.
Jsp page
<portlet:actionURL var="addEmployeeActionURL" name="addEmployee">
<portlet:param name="<%=ActionRequest.ACTION_NAME%>" value="addEmployee"/>
<portlet:param name="requestParam" value=" requestParamValue"/>
</portlet:actionURL>
<form action="<%=addEmployeeActionURL%>" name="emplyeeForm" method="POST">
Employee Name<br/>
<input type="text" name="<portlet:namespace/>employeeName" id="<portlet:namespace/>employeeName"/><br/>
Employee Address<br/>
<input type="text" name="<portlet:namespace/>employeeAddress" id="<portlet:namespace/>employeeName"/><br/>
<input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/>
</form>
Portlet Class Action Method
public class EmplyeePortletAction extends MVCPortlet {
public void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException {
String employeeName=ParamUtil.getString(actionRequest,"employeeName");
String employeeAddress=ParamUtil.getString(actionRequest,"employeeAddress");
String requestParamValue=ParamUtil.getString(actionRequest,"requestParam");
}
}
Solution:2
We can make it false to following tag value in liferay-portlet.xml file
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Solution:3
We can use alloy tag library form tags. When we use AUI tags it will append portlet name space to each input element name.
Jsp page
<%# taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<aui:input type="text" name="employeeAddress" id="employeeName"/><br/>
<aui:input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/
<input type="text" name="<portlet:namespace/>employeeAddress" id="<portlet:namespace/>employeeName"/>
Is same As
<aui:input type="text" name="employeeAddress" id="employeeName"/>
http://www.liferaysavvy.com/2013/12/liferay-spring-portlet.html
http://www.liferaysavvy.com/2014/04/liferay-mvc-portlet-development.html
Is there a way to forward a POST request from one controller to another with additional param(s)?
Let's say I have a form like this:
<form action"${contextPath}/controller1/post">
<input name="field1" type="text"/>
<input name="field2" type="text"/>
<input value="submit" type="submit"/>
</form>
This form will post to controller1.post() method.
But now I have another controller - controller2 also with a post method.
I now want to post to controller2.post so I could add some parameters to the request before forwarding to controller1.
Is there a way to do this?
You can try that if this is what you are looking for
#RequestMapping(value = "/controller1/{id}", method = RequestMethod.Post)
public void doSomething(
#PathVariable Long id,
HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("id",Id);
RequestDispatcher rd = request.getRequestDispatcher("your url/controller2");
rd.forward(request, response);
}
And after in controller2
#RequestMapping(value = "/controller2", method = RequestMethod.Post)
public string doSomething2(Model model,
HttpServletRequest request,
HttpServletResponse response) {
model.addAttribute("id", request.getAttribute("id"));
return "myView";
}
In my image.jsp, it will display a facebook photo. I can click on some features like "Darken Image" for example.
<form name="frmImage" action="image" method="post">
<input type="hidden" name="source" value="${param.source}" />
<input type="submit" value="Darken" name="darken" />
</form>
Inside my ImageServlet, I will then do the image processing for darkening the image. I made used of BufferedImage.
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
String source= req.getParameter("source");
BufferedImage img = null;
URL url = new URL(source);
img = ImageIO.read(url);
if(req.getParameter("darken") != null){
//do the processing
this.darkenImage(img);
}
getServletConfig().getServletContext().getRequestDispatcher("/image.jsp").forward(req, resp);
}
Now comes the issue, how am I suppose to return back the processed image back to my jsp page?
Reverse the chain of steps:
In the servlet: merely prepare an "image URL" calling a processing servlet with darken/url etc.
In the JSP an <img src="imageprocess?param1=...¶m2=...">.
In the image processing servlet use ImageIO.write(response.getOutputStream()).
Whether or not to use the first servlet is your decision.
Do not forget response.setContentType("image/jpeg"); or so.
I am using Spring security for authenticating users. I created a custom authentication provider and now I am wondering how I can get error messages from the provider into my form. This is the authenticate() method in my custom authentication provider:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UserProfile profile = userProfileService.findByEmail(authentication.getPrincipal().toString());
if(profile == null){
throw new UsernameNotFoundException(String.format("Invalid credentials", authentication.getPrincipal()));
}
String suppliedPasswordHash = DigestUtils.shaHex(authentication.getCredentials().toString());
if(!profile.getPasswordHash().equals(suppliedPasswordHash)){
throw new BadCredentialsException("Invalid credentials");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(profile, null, profile.getAuthorities());
return token;
}
This is my form:
<form name='f' action="<c:url value='j_spring_security_check' />" method='POST'>
<div id="data-entry-form">
<div class="form-entry">
<label><spring:message code="login.form.label.email"/></label>
<input type='text' name='j_username' value=''>
</div>
<div class="form-entry">
<label><spring:message code="login.form.label.password"/></label>
<input type='password' name='j_password'/>
</div>
<div class="form-entry">
<input type="submit" value="Verzenden"/>
</div>
</div>
How would I get error messages into my form? From the moment I press the login button, Spring takes over, so the only method I could generate error messages in would be the authenticate() method...
3 Steps of the safest way (we don't rely on the LAST_EXCEPTION):
Specify error page (for example "login-error") in configuration for your custom authentication provider
httpSecurity
.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/img/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.failureUrl("/login-error")
.and()
.logout().permitAll()
Create controller for url /login-error that returns view of your custom login page (for example "login") with the next code:
#Controller
public class LoginController {
#GetMapping("/login-error")
public String login(HttpServletRequest request, Model model) {
HttpSession session = request.getSession(false);
String errorMessage = null;
if (session != null) {
AuthenticationException ex = (AuthenticationException) session
.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
if (ex != null) {
errorMessage = ex.getMessage();
}
}
model.addAttribute("errorMessage", errorMessage);
return "login";
}
}
Get the error message into your page finally (ThymeLeaf tags for example):
<!--/*#thymesVar id="errorMessage" type="java.lang.String"*/-->
<div class="alert" th:if="${errorMessage}" th:text="${errorMessage}"></div>
I was able to solve it like this:
<c:if test="${param.auth eq 'failure'}">
<div class="error">
<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
</div>
</c:if>
Note that you need to detect whether there was an error via a special parameter which you can set in your spring-security config like this:
<security:form-login [...] authentication-failure-url="/login?auth=failure" />
EDIT:
Actually, passing that parameter is not necessary. Instead, one can simply check whether SPRING_SECURITY_LAST_EXCEPTION.message is defined, like this:
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION.message}">
<div class="error">
<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
</div>
</c:if>
I think that you should be able to get the messages in the same way than using the "standard" authenticators.
If an exception (or more than one) is thrown in the authentication process, the last exception is stored in a session attribute: SPRING_SECURITY_LAST_EXCEPTION.
So, to get the last exception message from the JSP you can use something like this:
<%=((Exception) request.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION")).getMessage()%>;
Of course, if you have a controller you should probably get the message from the controller and pass only the string to the jsp. This is only an example ;)