scala学习十 servlet开发
举个小例子,先熟悉一下:import javax.servlet.http.{HttpServlet,HttpServletRequest => HSReq, HttpServletResponse => HSResp}class HelloScalaServlet extends HttpServlet{override def doGet(req : HSReq, res
举个小例子,先熟悉一下:
import javax.servlet.http.{HttpServlet,
HttpServletRequest => HSReq, HttpServletResponse => HSResp}
class HelloScalaServlet extends HttpServlet
{
override def doGet(req : HSReq, resp : HSResp) =
resp.getWriter().print("<HTML>" +
"<HEAD><TITLE>Hello, Scala!</TITLE></HEAD>" +
"<BODY>Hello, Scala! This is a servlet.</BODY>" +
"</HTML>")
}
这个与Java的版本很像,是不是很熟悉
注意,我使用了一些适当的导入别名来缩短请求的类型名称和相应类型;除此之外,这个 servlet 几乎与其 Java servlet 形式一样。编译时请记得在 servlet-api.jar(通常随 servlet 容器一起发布;在 Tomcat 6.0 发行版中,它隐藏在 lib 子目录中)中包含一个引用,否则将找不到 servlet API 类型。
这还准备得不够充分;根据 servlet 规范,它必须使用一个 web.xml 部署描述符部署到 Web 应用程序目录中(或一个 .war 文件中),该描述符描述 servlet 应该与哪个 URL 结合。对于这样一个简单的例子,使用一个相当简单的 URL 来配合它最容易:
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>helloWorld</servlet-name>
<servlet-class>HelloScalaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloWorld</servlet-name>
<url-pattern>/sayHello</url-pattern>
</servlet-mapping>
</web-app>
当然,格式良好的 HTML 与格式良好的 XML 非常相似;鉴于这一点,Scala 对 XML 字面值的支持使编写这个 servlet 简单得多(参阅
参考资料
中的 “Scala 和 XML” 一文)。Scala 不是在传递给
HttpServletResponse
的 String 中直接嵌入消息,它可以分离逻辑和表示形式(非常简单),方法是利用此支持将消息放在 XML 实例中,然后再传递回去:
import javax.servlet.http.{HttpServlet,
HttpServletRequest => HSReq, HttpServletResponse => HSResp}
class HelloScalaServlet extends HttpServlet
{
def message =
<HTML>
<HEAD><TITLE>Hello, Scala!</TITLE></HEAD>
<BODY>Hello, Scala! It's now { currentDate }</BODY>
</HTML>
def currentDate = java.util.Calendar.getInstance().getTime()
override def doGet(req : HSReq, resp : HSResp) =
resp.getWriter().print(message)
}
实际上,Scala 编译器与 XML 对象消息一起整合到一个 scala.xml.Node
中,然后在将它传递给响应的 Writer
的 print
方法时将其转换为一个 String。
不要小看这一点 — 表达形式从逻辑中分离出来完全在一个类内部进行。这条 XML 消息将进行编译时检查,以确保语法正确和格式良好,并获得一些标准 servlet(或 JSP)不具备的好处。由于 Scala 可以进行类型推断,因此可以省略有关 message
和 currentDate
的实际类型消息,使得这就像动态语言 Groovy/Grails 一样。初次使用效果不错。
在servlet中获得客户端发来的参数
这个方法不会在任何用户界面设计大赛中夺冠,但是它达到了目的:这是一个 HTML 表单,它会将数据发送给一个新的 Scala servlet(绑定到 sayMyName 的相对 URL)。这些数据将根据 servlet 规范存储在一个名称-值对集合中,可通过 HttpServletRequest.getParameter()
API 调用获得。在此调用中,我们将 FORM
元素的名称作为一个参数传递给 API 调用。
class NamedHelloWorldServlet1 extends HttpServlet
{
def message(firstName : String, lastName : String) =
<HTML>
<HEAD><TITLE>Hello, {firstName} {lastName}!</TITLE></HEAD>
<BODY>Hello, {firstName} {lastName}! It is now {currentTime}.</BODY>
</HTML>
def currentTime =
java.util.Calendar.getInstance().getTime()
override def doPost(req : HSReq, resp : HSResp) =
{
val firstName = req.getParameter("firstName")
val lastName = req.getParameter("lastName")
resp.getWriter().print(message(firstName, lastName))
}
}
但这缺少了我之前提到的消息分离的一些好处,因为现在消息定义必须显式使用参数
firstName
和
lastName
;如果响应 get 中使用的元素个数超过 3 个或 4 个,情况就会变得比较复杂。此外,
doPost
方法在将参数传递给消息进行显示之前,必须自行提取每一个参数 — 这样的编码很繁琐,并且容易出错。
abstract class BaseServlet extends HttpServlet
{
import scala.collection.mutable.{Map => MMap}
def message : scala.xml.Node;
protected var param : Map[String, String] = Map.empty
protected var header : Map[String, String] = Map.empty
override def doPost(req : HSReq, resp : HSResp) =
{
// Extract parameters
//
val m = MMap[String, String]()
val e = req.getParameterNames()
while (e.hasMoreElements())
{
val name = e.nextElement().asInstanceOf[String]
m += (name -> req.getParameter(name))
}
param = Map.empty ++ m
// Repeat for headers (not shown)
//
resp.getWriter().print(message)
}
}
class NamedHelloWorldServlet extends BaseServlet
{
override def message =
<HTML>
<HEAD><TITLE>Hello, {param("firstName")} {param("lastName")}!</TITLE></HEAD>
<BODY>Hello, {param("firstName")} {param("lastName")}! It is now {currentTime}.
</BODY>
</HTML>
def currentTime = java.util.Calendar.getInstance().getTime()
}
当然,错误处理是处理 Web 应用程序
FORM
的重要部分,而 Scala 作为一种函数性语言,保存的内容都是表达式,这意味着我们可以将消息编写为结果页面(假设我们喜欢这个输入),或编写为错误页面(如果我们不喜欢这个输入)。因此,检查
firstName
和
lastName
的非空状态的验证函数可能
class NamedHelloWorldServlet extends BaseServlet
{
override def message =
if (validate(param))
<HTML>
<HEAD><TITLE>Hello, {param("firstName")} {param("lastName")}!
</TITLE></HEAD>
<BODY>Hello, {param("firstName")} {param("lastName")}!
It is now {currentTime}.</BODY>
</HTML>
else
<HTML>
<HEAD><TITLE>Error!</TITLE></HEAD>
<BODY>How can we be friends if you don't tell me your name?!?</BODY>
</HTML>
def validate(p : Map[String, String]) : Boolean =
{
p foreach {
case ("firstName", "") => return false
case ("lastName", "") => return false
//case ("lastName", v) => if (v.contains("e")) return false
case (_, _) => ()
}
true
}
def currentTime = java.util.Calendar.getInstance().getTime()
}
注意,模式匹配可以使编写比较简单的验证规则变得很容易。利用模式匹配绑定到原始值(比如上一个例子),或者绑定到一个本地变量(比如我们要排除任何姓名中有 “e” 的人,比如上一个注释)。
显然,还有事情需要做!困扰 Web 应用程序的典型问题之一是 SQL 注入攻击,它由通过 FORM
传入的未转义 SQL 命令字符引入,并且在数据库中执行之前连接到包含 SQL 结构的原始字符串。使用 scala.regex 包中的正则表达式支持可以确认 FORM
验证是否正确。事实上,整个验证过程会提交给使用默认验证实现的基类,该验证实现默认情况下只返回 true(因为 Scala 是函数性语言,所以不要忽略好的对象设计方法)。
更多推荐
所有评论(0)