My favorites | 中文(简体) | Sign in

使用 JSP

虽然我们可以直接从 Java servlet 代码输出用户界面的 HTML,但随着 HTML 变得复杂,这将变得难以维护。最好使用模板系统,在模板系统中,用户界面在单独的文件中设计和实现,且使用占位符和逻辑来插入应用程序提供的数据。有许多可用于 Java 的模板系统,任何系统可都可和 App Engine 配合使用。

在本辅导手册中,将使用 JavaServer 页面 (JSP) 来实现留言簿的用户界面。JSP 是 servlet 标准的一部分。App Engine 自动在应用程序的 WAR 中编译 JSP 文件,并将其映射到网址路径。

你好,JSP!

我们的留言簿应用程序将字符串写入到输出流,但是这也可以编写为 JSP。让我们从将示例的最新版本移植到 JSP 开始。

war/ 目录中,创建一个名为 guestbook.jsp 的文件,内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.google.appengine.api.users.User" %>
<%@ page import="com.google.appengine.api.users.UserService" %>
<%@ page import="com.google.appengine.api.users.UserServiceFactory" %>

<html>
  <body>

<%
    UserService userService = UserServiceFactory.getUserService();
    User user = userService.getCurrentUser();
    if (user != null) {
%>
<p>Hello, <%= user.getNickname() %>! (You can
<a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p>
<%
    } else {
%>
<p>Hello!
<a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a>
to include your name with greetings you post.</p>
<%
    }
%>

  </body>
</html>

默认情况下,war/ 或名称以 .jsp 结尾的子目录(除 WEB-INF/ 以外)中的文件都自动映射到网址路径。网址路径是指向 .jsp 文件的路径,包括文件名。此 JSP 将自动映射到网址 /guestbook.jsp

对于留言簿应用程序,我们希望这是应用程序的主页,在访问网址 / 时显示。执行该操作的一个简单方式是在 web.xml 中声明 guestbook.jsp 是该路径的“欢迎”servlet。

编辑 war/WEB-INF/web.xml 并替换 <welcome-file-list> 中的当前 <welcome-file> 元素。确保从列表中删除 index.html,因为静态文件优先于 JSP 和 servlet。

    <welcome-file-list>
        <welcome-file>guestbook.jsp</welcome-file>
    </welcome-file-list>

提示:如果使用的是 Eclipse,编辑器可在“设计”模式下打开此文件。要将此文件作为 XML 编辑,请在框架的底部选择“源”标签。

停止然后启动开发服务器。访问以下网址:

应用程序显示 guestbook.jsp 的内容,如果用户已登录,则包括用户昵称。

当首次载入 JSP 时,开发服务器将 JSP 转换为 Java 源代码,然后将 Java 源编译为 Java 字节码。Java 源和编译的类保存到临时目录中。如果原 JSP 文件发生更改,则开发服务器将自动重新生成和编译 JSP。

当将应用程序上传到 App Engine 时,SDK 将所有 JSP 编译为字节码,并仅上传字节码。当您的应用程序在 App Engine 上运行时,该应用程序将使用编译的 JSP 类。

留言簿表单

留言簿应用程序将需要一个网络表单供用户发布新的问候,以及一种处理该表单的方式。表单的 HTML 将进入 JSP。表单的目标将是由新的 servlet 类 SignGuestbookServlet 处理的新的网址 /signSignGuestbookServlet 将处理表单,然后将用户的浏览器重定向回 /guestbook.jsp。现在,新的 servlet 仅将发布的消息写入到日志中。

编辑 guestbook.jsp,并在紧靠结束 </body> 标签的上方放置以下行:

  ...

  <form action="/sign" method="post">
    <div><textarea name="content" rows="3" cols="60"></textarea></div>
    <div><input type="submit" value="Post Greeting" /></div>
  </form>

  </body>
</html>

guestbook 包中新建一个名为 SignGuestbookServlet 的类。(非 Eclipse 用户,在 src/guestbook/ 目录中创建 SignGuestbookServlet.java 文件。)为源文件提供以下内容:

package guestbook;

import java.io.IOException;
import java.util.logging.Logger;
import javax.servlet.http.*;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class SignGuestbookServlet extends HttpServlet {
    private static final Logger log = Logger.getLogger(SignGuestbookServlet.class.getName());

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();

        String content = req.getParameter("content");
        if (content == null) {
            content = "(No greeting)";
        }
        if (user != null) {
            log.info("Greeting posted by user " + user.getNickname() + ": " + content);
        } else {
            log.info("Greeting posted anonymously: " + content);
        }
        resp.sendRedirect("/intl/zh-CN/guestbook.jsp");
    }
}

编辑 war/WEB-INF/web.xml,添加以下行来声明 SignGuestbookServlet servlet 并将其映射到 /sign 网址:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
    ...

    <servlet>
        <servlet-name>sign</servlet-name>
        <servlet-class>guestbook.SignGuestbookServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>sign</servlet-name>
        <url-pattern>/sign</url-pattern>
    </servlet-mapping>

    ...
</web-app>

此新的 servlet 使用 java.util.logging.Logger 类将消息写入日志中。您可以使用 logging.properties 文件和在应用程序的 appengine-web.xml 文件中设置的系统属性来控制此类的行为。如果使用的是 Eclipse,您创建的应用程序在应用程序的 src/ 中具有此文件的默认版本,以及相应系统属性。

如果您没有使用 Eclipse,您必须手动设置日志记录器配置文件。将示例文件从 SDK appengine-java-sdk/config/user/logging.properties 复制到您的应用程序的 war/WEB-INF/ 目录中。然后编辑应用程序的 war/WEB-INF/appengine-web.xml 文件,如下所示:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    ...

    <system-properties>
        <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
    </system-properties>

</appengine-web-app>

servlet 日志消息使用 INFO 日志级别(使用 log.info())。默认日志级别是 WARNING,其禁止输出 INFO 消息。要更改 guestbook 包中的所有类的日志级别,请编辑 logging.properties 文件并为 guestbook.level 添加一个条目,如下所示:

.level = WARNING
guestbook.level = INFO

...

提示:当您的应用程序在 App Engine 上运行时使用 java.util.logging.Logger API 记录消息时,App Engine 记录这些消息并使其可在管理控制台上浏览,并可使用 AppCfg 工具下载。管理控制台允许您按日志级别浏览消息。

重新构建并重新启动,然后测试 http://localhost:8080/。将显示表单。在表单中输入一些文本,然后提交。浏览器将表单发送至应用程序,然后重定向回空表单。您输入的问候数据将由服务器记录到控制台中。

下一步...

我们有提示用户输入问候的用户界面。现在,我们需要一种方法来记住用户发布的问候,并将其显示给其他访问者。为此,我们将使用 App Engine 数据存储区。

继续转至将数据存储区与 JDO 配合使用