EL表达式
在JSP开发中,为了获取Servlet域对象中存储的数据,经常需要书写很多Java代码,这样的做法会使JSP页面混乱,难以维护,为此,在JSP2.0规范中提供了EL表达式。EL是Expression Language的缩写,它是一种简单的数据访问语言。
初识EL
由于EL可以简化JSP页面的书写,因此,在JSP学习中,掌握EL是相当重要的,要使用EL表达式,首先要学习它的语法。EL表达式的语法非常简单,都是以"${“开始,以”}"符号结束。
${}
需要注意的是,${}中的表达式必须符合EL语法要求。
EL语法
EL中的标识符
在EL表达式中,经常需要使用一些符号来标记一些名称,如变量名、如变量名、自定义函数名等,这些符号被称为标识符。EL表达式中的标识符可以由任意顺序的大小写字母、数字和下划线组成,为了避免出现非法的标识符,在定义标识符时还需要遵循以下规范:
1)不能以数字开头;
2)不能是EL中的保留字,如and、or、gt;
3)不能是EL隐式对象,如pageContext;
4)不能包含单引号(’),双引号(")、减号(-)和正斜线等特殊字符。
EL中的保留字
and eq gt true instanceof
or ne le false empty
not lt ge null div mod
EL中的变量
EL表达式中的变量就是一个基本的存储单元,EL表达式可以将变量映射到一个对象上
${product}
在上述示例中,product就是一个变量。EL表达式中的变量不用事先定义就可以直接使用,例如,表达式${product}就可以访问变量product的值。
EL中的常量
EL表达式中的常量又称字面量,它是不能改变的数据。在EL表达式包括多种常量
布尔常量
true和false
整数常量
整数常量与Java中的十进制的整数常量相同,它的取值范围是Java语言中定义的常量Long.MIN_VALUE到Long.MAX_VALUE之间,即(-2)62~262-1之间的整数。
浮点数常量
浮点数用整数部分加小数部分表示,也可以指数形式表示,例如,1.2E4和1.2都是合法的浮点数常量。它的取值范围是Java语言中定义的常量Double.MIN_VALUE到Double.MAX_VALUE之间,即4.9E-324~1.8E308之间的整数。
字符串常量
字符串常量是用单引号或双引号引起来的一连串字符。由于字符串常量。由于字符串常量需要用单引号或双引号引起来,所以字符串本身包括的单引号和双引号需要用反斜线(\)进行转义。
Null常量
Null常量用于表示变量引用的对象为空,它只是一个值,用null表示。
EL中的运算符
EL表达式支持简单的运算,例如,加(+)、减(-)、乘(*)、除(/)等。为此,在EL中提供了多种运算符,根据运算方法的不同,EL中的运算符包括以下几种。
1、点运算符(.)
EL表达式中的点运算符,用于访问JSP页面中某些对象的属性,如JavaBean对象、List对象、Array对象等,其语法格式如下
${customer.name}
在上述语法格式中,表达式${customer.name}中点运算符的作用就是访问customer对象中的name属性。
2、方括号运算符([])
与点运算的功能相同,都用于访问JSP页面中某些对象的属性,当获取的属性名中包含一些特殊字符,如"-“或”?"等非字母或数字的符号,就只能使用方括号运算符来访问该属性。
${user["name"]}
需要注意的是,在访问对象的属性时,通常情况都会使用点运算符作为简单的写法,但,实际上方括号运算符比点运算符应用的更广泛。
方括号运算符可以访问List集合或数组中指定索引的某个元素,如表达式${user[0]}用于访问集合或数组中的第一个元素。这种情况下,只能使用方括号运算符,而不能使用点运算符。
3、算数运算符
EL表达式中的算数运算符用于对整数和浮点数的值进行算数运算。使用这些算数运算符可以非常方便地在JSP页面进行算术运算,并且可以简化页面地代码量。
4、比较运算符
EL表达式中的比较运算符用于比较两个操作数的大小,操作数可以是各种常量、EL变量或EL表达式,所有的运算符执行的结果都是布尔类型。
5、逻辑运算符
EL表达式中的逻辑运算用于结果为布尔类型的表达式进行运算,运算的结果仍为布尔类型。
6、empty运算符
EL表达式中的empty运算符用于判断某个对象是否为null为"",结果为布尔类型
${empty var}
需要注意的是,empty运算符可以判断变量是否为null或""。例如,下列情况empty运算符的返回值为true。
1)var变量不存在,即没有定义,例如表达式${empty name},如果不存在name变量,就返回true。
2)var变量的值为null,例如表单式${empty customer.name},如果不存在cutomer.name的值为null,就返回true。
3)var变量引用集合(Set、Map和List)类型对象,并且在集合对象中不包含任何元素。
7、条件运算符
EL表达式中条件运算符用于执行某种条件判断
${A?B:C}
8、()运算符
EL表达式中的圆括号用于改变其他运算符的优先级,例如表达式${ab+c},正常情况下会先计算ab的积,然后再将计算的结果与c相加,如果在这个表达式中加一个圆括号运算符,将表达式修改为${a*(b+c)},这样则先计算b与c的和,再将计算的结果与a相乘。
注:再EL表达式取值时,没有数组的下标越界,没有空指针异常,没有字符串拼接。
EL隐式对象
隐式对象名称 | 描述 |
---|---|
pageContext | 对应于JSP页面中的pageContext对象 |
pageScope | 代表page域中用于保存属性的Map对象 |
requestScope | 代表request域中用于保存属性的Map对象 |
sessionScope | 代表session域中用于保存属性的Map对象 |
applicationScope | 代表applicationScope域中用于保存属性的Map对象 |
param | 代表一个保存了所有请求参数的Map对象 |
paramValues | 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string类型数组 |
header | 表示一个保存所有http请求头字段的Map对象 |
headerValues | 表示一个保存了所有http请求头字段的Map对象,返回string类型数组 |
cookie | 用来取得使用者的cookie值,cookie的类型是Map |
initParam | 表示一个保存了所有Web应用初始化参数的Map对象 |
pageContext可以获取其他10个隐式对象。
pageContext对象
为了获取JSP页面的隐式对象,可以使用EL表达式中的pageContext隐式对象。pageContext隐式对象的示例代码如下
${pageContext.reponse.characterEncoding}
上述实例中,pageContext对象用于获取response对象中的characterEncoding属性
pageContext.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
请求URL为:${pageContext.request.requestURI }<br>
<%=((HttpServletRequest)pageContext.getRequest()).getRequestURL() %><br>
Context-Type响应头:${pageContext.response.contentType }<br>
<%=((HttpServletResponse)pageContext.getResponse()).getContentType() %><br>
服务器信息为:${pageContext.servletContext.serverInfo }<br>
<%=pageContext.getServletContext().getServerInfo() %><br>
Servlet注册名为:${pageContext.servletConfig.servletName }<br>
<%=pageContext.getServletConfig().getServletName() %><br>
</body>
</html>
需要注意的是,不要将EL表达式和JSP中的隐式对象搞混,只有pageContext对象是两者共有的。
Web域相关对象
在Web开发中,PageContext、HttpServletRequest、HttpSession、ServletContext这四个对象之所以可以存储数据,是因为他们内部都定义了一个Map集合,这些Map集合是有一定作用范围的。习惯性地,我们把这些Map集合称为域,这些Map集合所在地对象称为域对象。
${pageScope.username}
${requestScope.username}
${sessionScope.username}
${applicationScope.username}
需要注意地是EL表达式只能在这4个作用域中获取数据。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
session.setAttribute("st3", "session");
request.setAttribute("st2", "request");
pageContext.setAttribute("st1", "page");
application.setAttribute("st4", "application");
String st5="self";
session.setAttribute("st6", "session");
request.setAttribute("st6", "request");
pageContext.setAttribute("st6", "page");
application.setAttribute("st6", "application");
%>
${st1 }
${pageScope.st1 }<br>
${st2 }
${requestScope.st2 }<br>
${st3 }
${sessionScope.st3 }<br>
${st4 }
${applicationScope.st4 }<br>
<%="st5=" %>
${st5 }<br>
如果变量名冲突:${st6 }<br>
</body>
</html>
如果不指定查找域,直接引用域中的属性名,EL会在page、request、session、application这4个作用域中按顺序依次查找属性。
param和paramValues对象
在JSP页面中,经常需要获取客户端传递的请求参数,为此,EL表达式提供了param和paramValues两个隐式对象专门用于获取客户端访问JSP页面时传递的请求参数。
1、param对象
param对象用于获取请求参数的某个值,它是Map类型,与request.getParameter()方法相同,在使用EL获取参数时,如果参数不存在,返回的是空字符串,而不是null。
${param.num}
2、paramValues对象
如果一个请求有多个值,可以使用paramValues对象来获取请求参数的所有值,该对象用于返回请求参数所有值组成的数组,如果要获取某个请求参数的第一个值
${paramValue.nums[0]}
1)param.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body style="text-align:center">
<form action="${pageContext.request.contextPath}/param.jsp">
num1:<input type="text" name="num1"><br>
num2:<input type="text" name="num"><br>
num3:<input type="text" name="num"><br>
<input type="submit" value="提交"/><hr>
num1:${param.num1 }<br>
num2:${paramValues.num[0]}<br>
num3:${paramValues.num[1]}<br>
</form>
</body>
</html>
header和headerValues对象
当客户端访问Web服务器中的JSP页面时,会通过请求消息头传递一些信息,例如请求消息头中的“User-Agent”字段可以告诉服务器浏览器的类型。为了获取请求消息头中的信息,EL表达式提供了两个隐式对象header和headerValues。
1、header对象
用于获取请求头字段的某个值
${header["user-agent"]}
2、headerValues对象
该对象用于返回请求头字符的所有值组成的数组
${headerValues["Accept-Language"][0]}
Cookie对象
在JSP开发中,经常需要获取客户端的Cookie信息,为此在EL表达式中,提供了Cookie隐式对象,该对象是一个代表所有Cookie信息的Map集合,Map集合中元素的键为各个Cookie的名称,值则为对应的Cookie对象
获取cookie对象的信息:${cookie.userName}
获取cookie对象的名称:${cookie.userName.name}
获取cookie对象的值:${cookie.userName.value}
第一次访问没有cookie,第二次访问画面
initParam对象
在开发一个Web应用程序中,通常会在web.xml文件中配置一些初始化参数,为了便于获取这些参数,EL表达式提供了一个initParam隐式对象,该对象可以获取Web应用程序中全局初始化参数
${initParam.count}
1)在web.xml中的<web-app>元素下增加一个<context-param>子元素
<context-param>
<param-name>webSite</param-name>
<param-value>www.baidu.com</param-value>
</context-param>
2)initparam.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
初始化参数webSite的值为:<br>
${initParam.webSite }
</body>
</html>
自定义EL函数
EL表达式简化了JSP页面的书写,使不懂Java编程的开发人员也可以编写强大的JSP页面。但EL表达式本身功能有限,例如,对于循环语句,EL表达式就很难实现。因此,EL表达式运行用户自定义EL函数。
HTML注入
在JSP开发中,经常会提交一些包含"<"、">"等特殊HTML字符的数据,如果程序不对这些特殊字符进行转换,浏览器会把这些字符当作HTML标签进行解释执行,这就是所谓的HTML注入。
案例_自定义EL函数防止HTML注入
1)开发自定义EL函数,首先需要编写一个执行自定义函数功能的Java类,编写的Java类必须定义为public,并且作为函数的方法必须声明为public static类型。接下来编写一个Java类,用于实现HTML编码转换。
HTMLFilter.java
package EL;
public class HTMLFilter {
public static String filter(String message){
if(message==null){
return(null);
}
char content[]=new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuffer result=new StringBuffer(content.length+50);
for(int i=0;i<content.length;i++){
switch(content[i]){
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return result.toString();
}
}
2)为了让Java类的静态方法可以被EL表达式调用,需要在一个标签库描述符(tld)文件中对EL自定义函数进行描述,将Java类的静态方法映射成一个EL自定义函数,接下来编写一个描述自定义EL函数的mytagkib.tld文件,该文件放置到WEB-INF目录中或WEB-INF目录下的除了classes和lib目录之外的任意子目录中。
mytagkib.tld
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<description>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>c</short-name>
<uri>http://myFunction.com</uri>
<function>
<name>filter</name>
<function-class>
EL.HTMLFilter
</function-class>
<function-signature>
java.lang.String filter(java.lang.String)
</function-signature>
</function>
</taglib>
3)result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="myFunction" uri="http://myFunction.com" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
String message="<script type='text/javascript'>alert(1)</script>";
pageContext.setAttribute("message", message);
%>
用户名:$(name)<br/>
留言信息:${myFunction:filter(message) }
</body>
</html>
可以看到原本的HTML内容被显示了出来,避免了HTML注入。