部署描述符:web.xml

区域 ID

REGION_ID 是 Google 根据您在创建应用时选择的区域分配的缩写代码。此代码不对应于国家/地区或省,尽管某些区域 ID 可能类似于常用国家/地区代码和省代码。对于 2020 年 2 月以后创建的应用,REGION_ID.r 包含在 App Engine 网址中。对于在此日期之前创建的现有应用,网址中的区域 ID 是可选的。

详细了解区域 ID

Java 网页应用使用部署描述符文件来确定如何将网址映射到 servlet、哪些网址需要身份验证以及其他信息。该文件名为 web.xml,是 Web 应用的 servlet 规范的一部分。 如需详细了解 web.xml 部署描述符,请参阅 Servlet 规范

如果您要从 Java 8 迁移,并且需要将旧版捆绑服务与最新支持的 Java 版本搭配使用,则必须在 web.xml 文件中添加 <app-engine-apis> 元素并将其设置为 true

<app-engine-apis>true</app-engine-apis>

部署描述符

网页应用的部署描述符描述了应用的类、资源和配置,以及网络服务器如何使用它们处理网络请求。当网络服务器收到应用请求时,它使用部署描述符将请求网址映射到应处理该请求的代码。

部署描述符是一个名为 web.xml 的文件。它位于应用 WAR 的 WEB-INF/ 目录下。文件是一个根元素为 <web-app> 的 XML 文件。

以下 web.xml 示例针对 EE10 上的版本 21 及更高版本(默认)、EE8 上的版本 21 和版本 17 及更低版本将所有网址路径 (/*) 映射到 servlet 类 mysite.server.ComingSoonServlet。如需在默认配置上使用受支持的最新版本,您必须更新应用 servlet 和依赖项以包含 Jakarta 命名空间。如需详细了解配置选项,请参阅升级现有应用

v21 及更高版本 (EE10)

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
     version="6.0">
    <runtime>java21</runtime> <!-- or another supported version -->
    <servlet>
        <servlet-name>comingsoon</servlet-name>
        <servlet-class>mysite.server.ComingSoonServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>comingsoon</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

v21 (EE8)

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
        version="3.1">
   <runtime>java21</runtime>

  <system-properties> <!-- run your apps on EE8  -->
      <property name="appengine.use.EE8" value="true"/>
  </system-properties>

    <servlet>
        <servlet-name>comingsoon</servlet-name>
        <servlet-class>mysite.server.ComingSoonServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>comingsoon</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

v17 及更低版本

  <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
          version="3.1">
      <runtime>java17</runtime>
      <servlet>
          <servlet-name>comingsoon</servlet-name>
          <servlet-class>mysite.server.ComingSoonServlet</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>comingsoon</servlet-name>
          <url-pattern>/*</url-pattern>
      </servlet-mapping>
  </web-app>

如果您将应用分解为多项服务,则每项服务都有自己的配置参数。

Servlet 和网址路径

web.xml 定义网址路径与通过这些路径来处理请求的 servlet 之间的映射。网络服务器使用此配置来标识用于处理指定请求的 servlet,以及调用与请求方法相对应的类方法。例如:HTTP GET 请求的 doGet() 方法。

要将网址映射到 Servlet,您需要用 <servlet> 元素声明 Servlet,然后通过 <servlet-mapping> 元素定义从网址路径到 Servlet 声明的映射。

<servlet> 元素会声明 Servlet,包括文件中的其他元素用于引用 Servlet 的名称、用于 Servlet 的类以及初始化参数。您可以使用具有不同初始化参数的同一类声明多个 servlet。每个 servlet 的名称在部署描述符中必须是唯一的。

    <servlet>
        <servlet-name>redteam</servlet-name>
        <servlet-class>mysite.server.TeamServlet</servlet-class>
        <init-param>
            <param-name>teamColor</param-name>
            <param-value>red</param-value>
        </init-param>
        <init-param>
            <param-name>bgColor</param-name>
            <param-value>#CC0000</param-value>
        </init-param>
    </servlet>

    <servlet>
        <servlet-name>blueteam</servlet-name>
        <servlet-class>mysite.server.TeamServlet</servlet-class>
        <init-param>
            <param-name>teamColor</param-name>
            <param-value>blue</param-value>
        </init-param>
        <init-param>
            <param-name>bgColor</param-name>
            <param-value>#0000CC</param-value>
        </init-param>
    </servlet>

<servlet-mapping> 元素会指定一个网址模式以及网址与该模式匹配的请求所使用的已声明 Servlet 的名称。网址格式可以在格式的开头或结尾使用星号 (*),以指示零个或多个任意字符。网址格式不支持在字符串中间使用通配符,并且不允许在一个格式中使用多个通配符。模式会与网址的完整路径匹配,从域名后面的正斜线 (/) 开始(含正斜线)。网址路径不能以英文句点 (.) 开头。

    <servlet-mapping>
        <servlet-name>redteam</servlet-name>
        <url-pattern>/red/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>blueteam</servlet-name>
        <url-pattern>/blue/*</url-pattern>
    </servlet-mapping>

在此示例中,TeamServlet 类处理了网址 http://www.example.com/blue/teamProfile 的请求,其中 teamColor 参数等于 bluebgColor 参数等于 #0000CC。Servlet 可以使用 ServletRequest 对象的 getPathInfo() 方法获取网址路径中与通配符匹配的部分。

Servlet 可以访问其初始化参数,方法是使用自己的 getServletConfig() 方法获取其 Servlet 配置,然后以实参 (argument) 形式使用参数 (parameter) 名称来对配置对象调用 getInitParameter() 方法。

String teamColor = getServletConfig().getInitParameter("teamColor");

JSP

应用可以使用 JavaServer Pages (JSP) 来实现网页。JSP 是混合使用静态内容(如 HTML)和 Java 代码定义的 servlet。

App Engine 支持 JSP 自动编译和网址映射。应用 WAR 中文件名以 .jsp 结尾的 JSP 文件(在 WEB-INF/ 外部)自动编译为 Servlet 类,并映射到等效于从 WAR 根目录到 JSP 文件的路径的网址路径。例如,如果应用在其 WAR 的 register/ 子目录中包含一个名为 start.jsp 的 JSP 文件,App Engine 将编译该文件并将其映射到网址路径 /register/start.jsp

如果需要更好地控制如何将 JSP 映射到网址,您可以明确指定映射,只需在部署描述符中使用 <servlet> 元素进行声明即可。您可以使用从 WAR 根目录到 JSP 文件的路径来指定 <jsp-file> 元素,而不是使用 <servlet-class> 元素。JSP 的 <servlet> 元素可以包含初始化参数。

    <servlet>
        <servlet-name>register</servlet-name>
        <jsp-file>/register/start.jsp</jsp-file>
    </servlet>

    <servlet-mapping>
        <servlet-name>register</servlet-name>
        <url-pattern>/register/*</url-pattern>
    </servlet-mapping>

可以使用 <taglib> 元素来安装 JSP 标记库。标记库具有指向 JSP 标记库描述符 (TLD) 文件的路径 (<taglib-location>) 以及 JSP 用于选择要加载的库的 URI (<taglib-uri>)。请注意,App Engine 提供 JavaServer Pages 标准标记库 (JSTL),您无需安装。

    <taglib>
        <taglib-uri>/escape</taglib-uri>
        <taglib-location>/WEB-INF/escape-tags.tld</taglib-location>
    </taglib>

安全和身份验证

App Engine 应用可以使用 Google 账号进行用户身份验证。该应用可以使用 Google Accounts API 来检测用户是否已登录、获取当前已登录用户的电子邮件地址,以及生成登录和退出登录网址。应用还可以根据 Google 账号,使用部署描述符来为网址路径指定访问限制。

<security-constraint> 元素定义与格式匹配的网址的安全约束。如果用户访问的网址路径具有安全约束,并且用户没有登录,则 App Engine 会将用户重定向到 Google 账号登录页面。在用户成功登录或注册新账号后,Google 账号会重定向用户,使其返回应用网址。该应用无需执行任何其他操作,即可确保只有已登录用户才能访问该网址。

安全约束包括指定哪些 Google 账号用户可以访问该路径的授权约束。如果授权限制指定 * 用户角色,则使用 Google 账号登录的所有用户都可以访问该网址。如果该限制指定 admin 用户角色,则只有已注册的应用开发者才能访问该网址。有了 admin 角色,您就可以轻松构建仅限管理员访问的网站区域。

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>profile</web-resource-name>
            <url-pattern>/profile/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>admin</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

App Engine 不支持部署描述符中的自定义安全角色 (<security-role>) 或备用身份验证机制 (<login-config>)。

安全约束适用于静态文件以及 servlet。

安全网址

对于使用 REGION_ID.r.appspot.com 域名的网址,App Engine 支持使用 HTTPS 进行安全连接。当某个请求使用 HTTPS 访问网址,并且该网址已在 web.xml 文件中配置为使用 HTTPS 时,请求数据和响应数据都会在进行传输之前由发送方加密,然后由接收方在收到之后进行解密。安全连接对保护客户数据(如联系信息、密码和私信)非常有用。

要声明网址应使用 HTTPS,请在部署描述符中使用 <user-data-constraint>(其中 <transport-guarantee> 设置为 CONFIDENTIAL)设置安全约束(如安全和身份验证部分中所述)。例如:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>profile</web-resource-name>
            <url-pattern>/profile/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

如果请求使用 HTTP(非安全)来访问网址,而网址的传输保证设置为 CONFIDENTIAL,则此类请求将使用 HTTPS 自动重定向至同一网址。

任何网址均可使用 CONFIDENTIAL 传输保证,包括 JSP 和静态文件。

开发网络服务器不支持 HTTPS 连接。它会忽略传输保证,因此,可以使用到开发网络服务器的常规 HTTP 连接来测试专用于 HTTPS 的路径。

当您使用有版本控制的 appspot.com 网址(例如 https://1.latest.your_app_id.REGION_ID.r.appspot.com/)来测试应用的 HTTPS 处理程序时,您的浏览器会向您发出警告,表明未针对该特定网域路径签署 HTTPS 证书。如果您接受该网域的证书,网页将成功加载。用户在访问 https://your_app_id.REGION_ID.r.appspot.com/ 时将不会看到证书警告。

您还可以使用有版本控制的 appspot.com 网址的替代形式来避免这一问题,方法是使用字符串“-dot-”替换掉用于分隔各子网域组件的英文句点。例如,上一个示例网址可在 https://VERSION_ID-dot-default-dot-PROJECT_ID.REGION_ID.r.appspot.com 没有证书警告的情况下正常访问。

Google 账号的登录和退出始终是通过安全连接执行的,与应用网址配置为何种格式无关。

正如上面提到的一样,安全约束适用于静态文件以及 servlet。这包括传输保证。

注意:Google 建议使用 HTTPS 协议向应用发送请求。Google 不会为 appspot.com 上托管的双通配符网域签发 SSL 证书。因此,使用 HTTPS 时,您必须使用“-dot-”字符串(而非“.”)分隔子域名,如以下示例所示。您可以在自己的自定义域名或 HTTP 地址中简单使用一个“.”。

欢迎文件列表

当网站的网址表示指向 WAR 中的静态文件或 JSP 的路径时,通常最好让目录路径也提供一些有用的信息。 如果用户通过访问网址路径 /help/accounts/password.jsp 来获取账号密码的相关信息,则可能会尝试通过访问 /help/accounts/ 来查找介绍账号系统文档的页面。部署描述符可以指定一个文件名列表;在用户访问表示 WAR 子目录(尚未显式映射到 servlet)的路径时,服务器应尝试检查这些文件。Servlet 规范将其称为 welcome file list

例如,如果用户访问网址路径 /help/accounts/,则部署描述符中的以下 <welcome-file-list> 元素会告知服务器先检查 help/accounts/index.jsphelp/accounts/index.html,然后再报告网址不存在:

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

过滤

过滤器是一个对请求进行处理的类(类似于 servlet),但可允许使用其他过滤器或 servlet 继续处理请求。过滤器可以执行一个辅助任务,如日志记录、执行专用身份验证检查或在调用 servlet 之前为请求或响应对象添加注释。通过使用过滤器,您可以基于部署描述符编写请求处理任务。

以下过滤器实现示例会记录消息并将控制权沿链(可能包括其他过滤器或 servlet)向下传递,如 EE10 上的版本 21 及更高版本(默认)、EE8 上的版本 21 和版本 17 及更低版本的部署描述符所述。如需在默认配置上使用受支持的最新版本,您必须更新应用 servlet 和依赖项以包含 Jakarta 命名空间。如需详细了解配置选项,请参阅升级现有应用

v21 及更高版本 (EE10)

此过滤器类使用 doFilter() 方法实现 jakarta.servlet.Filter 接口。

package mysite.server;

import java.io.IOException;
import java.util.logging.Logger;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

public class LogFilterImpl implements Filter {

    private FilterConfig filterConfig;
    private static final Logger log = Logger.getLogger(LogFilterImpl.class.getName());

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {
        log.warning("Log filter processed a " + getFilterConfig().getInitParameter("logType")
            + " request");

        filterChain.doFilter(request, response);
    }

    public FilterConfig getFilterConfig() {
        return filterConfig;
    }

    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
    }

    public void destroy() {}

}

v21 (EE8)

此过滤器类使用 doFilter() 方法实现 javax.servlet.Filter 接口。

package mysite.server;

import java.io.IOException;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class LogFilterImpl implements Filter {

    private FilterConfig filterConfig;
    private static final Logger log = Logger.getLogger(LogFilterImpl.class.getName());

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {
        log.warning("Log filter processed a " + getFilterConfig().getInitParameter("logType")
            + " request");

        filterChain.doFilter(request, response);
    }

    public FilterConfig getFilterConfig() {
        return filterConfig;
    }

    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
    }

    public void destroy() {}

}

v17 及更低版本

此过滤器类使用 doFilter() 方法实现 javax.servlet.Filter 接口。

package mysite.server;

import java.io.IOException;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class LogFilterImpl implements Filter {

    private FilterConfig filterConfig;
    private static final Logger log = Logger.getLogger(LogFilterImpl.class.getName());

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {
        log.warning("Log filter processed a " + getFilterConfig().getInitParameter("logType")
            + " request");

        filterChain.doFilter(request, response);
    }

    public FilterConfig getFilterConfig() {
        return filterConfig;
    }

    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
    }

    public void destroy() {}

}

与 Servlet 类似,您可以在部署描述符中配置过滤器,方法是先使用 <filter> 元素声明过滤器,然后再通过 <filter-mapping> 元素将其映射到网址格式。您也可以将过滤器直接映射到其他 servlet。

<filter> 元素包含 <filter-name><filter-class> 和可选的 <init-param> 元素。

    <filter>
        <filter-name>logSpecial</filter-name>
        <filter-class>mysite.server.LogFilterImpl</filter-class>
        <init-param>
            <param-name>logType</param-name>
            <param-value>special</param-value>
        </init-param>
    </filter>

<filter-mapping> 元素包含 <filter-name>(与所声明过滤器的名称相匹配),以及 <url-pattern> 元素(用于将过滤器应用于网址)或 <servlet-name> 元素(与所声明 Servlet 的名称匹配以在调用 Servlet 时应用该过滤器)。

    <!-- Log for all URLs ending in ".special" -->
    <filter-mapping>
        <filter-name>logSpecial</filter-name>
        <url-pattern>*.special</url-pattern>
    </filter-mapping>

    <!-- Log for all URLs that use the "comingsoon" servlet -->
    <filter-mapping>
        <filter-name>logSpecial</filter-name>
        <servlet-name>comingsoon</servlet-name>
    </filter-mapping>

错误处理程序

通过使用部署描述符,您可以自定义在发生错误时服务器向用户发送的内容。在服务器将要发送特定 HTTP 状态代码或 servlet 引发特定 Java 异常时,服务器可以显示一个替代页面位置。

<error-page> 元素包含 <error-code> 元素(值为诸如 500 之类的 HTTP 错误代码),或 <exception-type> 元素(值为预期异常的类名称,如 java.io.IOException)。它还包含一个 <location> 元素,其包含发生错误时要显示的资源的网址路径。

    <error-page>
        <error-code>500</error-code>
        <location>/errors/servererror.jsp</location>
    </error-page>

您无法为以下错误条件配置自定义错误处理程序:

  • 没有为网址定义 servlet 映射时的 404 响应页面。
  • 403 配额错误页面
  • App Engine 内部错误后显示的 500 服务器错误页面。

不支持的 web.xml 功能

App Engine 不支持以下 web.xml 功能:

  • App Engine 支持使用 <load-on-startup> 元素进行 Servlet 声明。 但是,载入实际上是在网络服务器实例处理第一个请求期间发生的,而不是在之前发生。
  • 某些部署描述符元素可以采用直观易懂的显示名称、描述和图标,以便用于 IDE。App Engine 不使用这些内容,并会忽略它们。
  • App Engine 不支持 JNDI 环境变量 (<env-entry>)。
  • App Engine 不支持 EJB 资源 (<resource-ref>)。
  • 不支持有关销毁 Servlet、Servlet 上下文或过滤器的通知。
  • <distributable> 元素会被忽略。
  • 不支持使用 <run-at> 调度 Servlet。