Is this an acceptable case to use a JSP scriptlet?
I know that using JSP scriptlets is generally frowned upon, which is why I'm wondering if there is a more elegant way of achieving what I'm trying to do. Pretty much I am creating a view, and depending on certain situations in the domain model, I am rendering different HTML.
For example, consider the scenario where a user could be in a role. And a method like th开发者_如何学运维is is defined on the User model class:
public boolean isInRole(String roleName) {
// Logic here to determine if the user is in a role
}
And then we have a JSP, like below:
<%
User user = (User)request.getAttribute("user");
%>
// Some HTML here...
<% if (user.isInRole("admin") { %>
// Generate some admin menu here
<% } %>
As far as I know this can't be done using JSTL/EL. Does using scriptlets here sound like a good idea? Or is there another approach I should be taking?
Thanks for any suggestions.
I'd either upgrade to Servlet 3.0 / JSP 2.2 where invoking methods with arguments is allowed in EL
<c:if test="${user.isInRole('admin')}">
or create a custom EL function
<c:if test="${util:isUserInRole(user, 'admin')}">
As per your question history, you seem to be already using JEE6. So the first approach should work for you (if your web.xml
is declared conform Servlet 3.0 spec).
You may also create a custom tag to accomplish this. Here is a bare bones example:
create a new tld file: WEB-INF/user.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag
Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name></short-name>
<tag>
<name>user</name>
<tag-class>tags.UserRoleTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>roles</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
create the custom tag class: tags/UserRoleTag.java
package tags;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
@SuppressWarnings("serial")
public class UserRoleTag extends TagSupport {
private String roles;
public int doStartTag() throws JspException {
String userRole = (String)pageContext.getAttribute("currentUserRole");
return roles.contains(userRole) ? EVAL_BODY_AGAIN : SKIP_BODY;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
}
create your jsp: warfolder/home.jsp
<%@ taglib uri="/WEB-INF/user.tld" prefix="u" %>
<% pageContext.setAttribute("currentUserRole", "admin"); // this value would come from the controller... %>
<u:user roles="admin registered">
welcome admin!
</u:user>
<u:user roles="guest">
welcome guest!
</u:user>
Taking this approach keeps your model and tag loosely coupled, and it can be reused in other projects, most likely.
The rule of thumb that I use is that scriptlets (or JSP tags or EL for that matter) must only use the model that has already been set up by the controller as opposed to triggering actions that interact with back end systems and get stuff.
The difference is often subtle but is quite significant in my opinion. In your example, let us say that the determination if the user is in an admin role requires you to access some database or make a web service call. Then you would be violating MVC by triggering that action from a view. Whether you invoke the action from a scriptlet or a JSP tag or EL is immaterial. You have done stuff that is best done by the controller.
So how would I solve this problem? I would put the logic of determining if the user is in an admin role into the controller. Then I would make this information available in the model so that the view can use the model to determine if the user is in an admin role. A simple ${user.isAdmin} would then answer the question from the view. The view would not be "accused" of triggering any actions.
So, am I being a purist by being a stickler to this kind of injunction? I find that in many cases where an optimization of an application is required, it is easier for me to scan the controllers and optimize on what they are doing. If the view also triggers back end work, then it would be very hard to do this kind of optimization since the back end interaction would then be scattered across multiple controllers and views.
This also violates the single responsibility principle. Hope this answers your question.
By the way, great question. I am glad that you are thinking about this to this level of detail.
精彩评论