No
在前面,我们已经看到了JSP如何转换为Java代码,可以写Servlet那样写JSP,拥有强大的功能,那么是否应该在JSP中使用Java?一般不建议,JSP中的Java最大的问题就是太强大。Jsp主要用于表现层,也就是用户界面,即view。一个组织良好的项目,UI和后面的实现应该分离,由不同的人员进行编写。UI的程序员甚至可以不使用java代码。
另外对于一个面向对象的编程语言,不应该将所有的东西都放在一个jsp文件中,而是创建不同的类进行不同的处理。
存放位置和配置
WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录,class/就位于此目录。Jsp功能很强大,因此一般放在WEB-INF/jsp/目录下。
在前面的介绍中,我们需要为每个jsp文件设置其directive,很是麻烦,可以在web.xml中进行统一的设置,例子如下(颜色字体为标注,并非该web.xml文件的内容):
<?xml version="1.0" encoding="UTF-8"?>
<web-app …… >
<display-name>Hello World Application</display-name>
<jsp-config>
<!-- 1、jsp-config中可以有多组jsp-property-group,通过url-pattern来指定jsp文件,例如/WEB-INF/jsp/admin/*.jsp。-->
<!-- 本例是表示所有的jsp和jspf文件。-->
<!-- 2、如果同时匹配servlet-mapping和jsp-property-group的url-pattern,那么jsp-property-group中的优胜。-->
<!-- 如果都是jsp-property-group,那么最佳匹配优胜,即/WEB-INF/jsp/admin/*.jsp优胜于*.jsp。如果都一样,则按先后顺序。-->
<!-- 3、对于同时匹配不同的group,根据上述优先进行选择,但是<include-prelude>和<include-coda>的内容都会出作为该文件的开始。-->
<!-- 即属性是有优先级别,并选择最优匹配的那个;但是include则匹配多少个都加上去。-->
<!-- 4、为了避免不必要的失误,应尽量避免匹配多个group的情况。 -->
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspf</url-pattern>
<!-- 定义directive的pageEncoding -->
<page-encoding>UTF-8</page-encoding>
<!-- 之前讨论了是否应该在jsp中加入Java,缺省是运行,即下面的值为false,如果要禁止,scripting-invalid设置为true。-->
<!-- 同样的,<el-ignore>是用来禁止expression language,缺省运行使用,即缺省值为false -->
<scripting-invalid>false</scripting-invalid>
<!-- include-prelude表明在该group所有的jsp的开始都加上base.jspf,我们可以在base.jspf中定义通用的变量,tab库等等。-->
<!-- 使用<include-coda>则是将jspf文件加在jsp的后面。我们可以定义header.jspf和footer.jspf作为jsp模板的前后。-->
<include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
<!-- 这个设置很有用,在前面我们知道每个directive、declaration、scriplet和其他的jsp tags在HTML中会输出一个空行。-->
<!-- 设置true,会删除有关的输出,使得HTML代码简洁。-->
<trim-directive-whitespaces>true</trim-directive-whitespaces>
<!-- jsp的缺省内容类型是text/html,可以不设置。-->
<default-content-type>text/html</default-content-type>
</jsp-property-group>
</jsp-config>
</web-app>
此外<buffer>设置buffer属性,一般不作修改。
<error-undecleared-namespace>设置当tag的namespace无效时是否给出error,缺省是在给出的,即false。
<is-xml>表明匹配JSP的是JSP文档,这将在后面学习。
<deferred-syntax-allowed-as-literal>也将在后面学习(第6章)。
这些设置除了url-pattern外,其它都是可选的,但必须符合以下顺序:<url-pattern>,<el-ignored>,<page-encoding>,<scripting-invalid>,<is-xml>,<include-prelude>,<include-coda>,<deferred-syntax-allowed-as-literal>,<trim-directive-whitespace>,<default- content-type>,<buffer>, <error-on-undeclared-namespace>
我们将每个jsp包含的头部分,放到WEB-INF/jsp/中,这里确保浏览器不能直接访问。下面是这个base.jspf的例子:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="cn.wei.flowingflying.customer_support.Ticket, cn.wei.flowingflying.customer_support.Attachment" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
在Eclipse中,虽然集体定义了,但是在编写jsp中,不会自动关联到该jspf,如果我们在jsp文件中使用到了Ticket类,会有报错,但是不会影响运行结果,这点也是挺讨厌的,基本是不可接受的,所以一般不将import放在集体定义中。
定义了import的类和使用JSTL的core tag。下面是webapp/根下面的index.jsp,将其重定向到/tickets
<%@ page session="false" %>
<c:redirect url="/tickets" />
c:redirect就是使用了JSTL的core tag进行重定向。第一行要求session为false,否则在响应302会在Set-Cookie的消息头中给出:JSESSIONID,同时在Location中带上jsessionid。
浏览器接下来会向http://localhost:8080/customer-support/tickets;jsessionid=461180630E2AFB62C0C667C5E4814FDC发出请求。因此在重定向中,如果没有必要将jsessionid添加在url中,应该设置session为false。
将请求从Servlet转到jsp
合适的方式是servlet进行业务逻辑的处理,然后将UI呈现交给Jsp处理。
例子一:servlet进行分析后,输出某个静态jsp页面
private void showTicketForm(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp").forward(request, response);
}
ticketForm.jsp是个静态的jsp,放在/WEB-INF/目录下,用户不可以直接读取。
采用request.getRequestDispatcher(path).forward(request,response)的方式不是进行重定向,我们可以看到浏览器没有收到3xx的响应,而URL并没有变化。在进行forward之前或者之后,即使我们试图去写response,也不会出现在HTTP的响应中。
例子二:经过servlet分析后,输出到某个动态jsp页面
也就是我们需要将一些数据传递到jsp上,然后通过jsp呈现。在servlet中,可以通过Attribute进行数据的传递。例子如下:
private void viewTicket(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{
String idString = request.getParameter("ticketId");
Ticket ticket = this.getTicket(idString, response);
if(ticket == null)
return;
// 在Attribute中以对象方式存放信息,当request处理完,attributes也会被丢弃。
// 用于在程序的不同地方处理同一request时进行信息传递,例如在servlet和JSP之间
request.setAttribute("ticketId", idString);
request.setAttribute("ticket", ticket);
request.getRequestDispatcher("/WEB-INF/jsp/view/viewTicket.jsp").forward(request, response);
}
相应的viewTicket.jsp如下。
<%@ page import="cn.wei.flowingflying.customer_support.Ticket, cn.wei.flowingflying.customer_support.Attachment" %>
<%@ page session="false" %> <%-- 原本import属于共同定义,但是jsp上有告警,实在受不了,还是给自引入吧 --%>
<% //servlet在分析时,将数据放入Attribute,此处将数据取出
String ticketId = (String) request.getAttribute("ticketId");
Ticket ticket = (Ticket)request.getAttribute("ticket");
%>
<!DOCTYPE html>
<html>
<head>
<title>Customer Support</title>
</head>
<body>
<h2>Ticket #<%= ticketId %>: <%= ticket.getSubject() %></h2>
<i>Customer Name - <%= ticket.getCustomerName() %></i><br /><br />
<%= ticket.getBody() %><br /><br />
<%
if(ticket.getNumberOfAttachments() > 0){
%>Attachments: <%
int index = 0;
for(Attachment a: ticket.getAttachments()){
if(index ++ >0)
out.print(",");
%><%-- 下面是通过JSTL的core tag,构建链接,加入连接中GET的参数,如http://localhost:8080/customer-support/ tickets?action=download&ticketId=1&attachment=readme.txt --%>
<a href="<c:url value="/tickets">
<c:param name="action" value="download" />
<c:param name="ticketId" value="<%= ticketId %>" />
<c:param name="attachment" value="<%= a.getName() %>" />
</c:url>">
<%= a.getName() %></a><%
}
%><br /><br /> <%
} %>
<a href="<c:url value="/tickets" />">Return to list tickets</a>
</body>
</html>
通过Attribute,可以传递一些复制的对象,例如Map,下面是例子。由于转换到Map<Integer,Ticket>是一个没有检查的操作,进行了@SupressWarning(“unchecked”)。
<%@ page session="false" import="cn.wei.flowingflying.customer_support.Ticket, java.util.Map" %>
<%
@SuppressWarnings("unchecked")
Map<Integer,Ticket> ticketDatabase = (Map<Integer,Ticket>)request.getAttribute("ticketDatabase");
%>