注册用户享全站资源 并成为粉丝 不定时福利发放
 
非盈利性学习社区
分享网站

分享网站

让更多人可以获取免费教程

合作广告

合作广告

最大优惠

人事面试

人事面试

套路太深

面试过程中,面试官会向应聘者发问,而应聘者的回答将成为面试官考虑是否接受他的重要依据。对应聘者而言,了解这些问题背...

部分企业人事部收到求职者简历后,会预先进行一轮电话面试,来考察求职者的综合素质,因此了解懂得求职面试的基本技巧,将...

1、着装要适当  穿着不一定要名贵,但一定要合体、干净、整洁,而且颜色和图案的搭配一定要协调。鞋子应该是舒服而又引...

一、隐瞒真实个人资料的不诚实者简历是求职的第一步,只有面试官对你的简历有兴趣才会通知你面试。在简历中适当地突出个人...

一、首先你得已经成为公司里“最好”的程序员,或者你已经找不到可作为老师和导师的人关于这一点,很多人都会过度自信,所...

程序人生

程序人生

技术的另一面

程序员不是你们眼中的程序猿-后IT时代。程序猿是一种非常特殊的、可以从事程序开发、维护的动物。

1.某程序员的QQ签名:为API生,为框架死,为debug奋斗一辈子,吃符号亏,上大小写的当,最后死在需求上2.去...

Web应用,最常见的研发语言是Java和PHP。后端服务,最常见的研发语言是Java和C/C++。大数据,最常见的...

1、第一份工作的选择很重要。不要想着我没有选择的机会,有份工作就不错了,现实情况是进了一行,想出来很难,想转行更难...

       本文摘自一位工作五年程序老哥的演讲。演讲大概讲了他走过的五年时光,反思自己的职业工作,反思自己的生...

程序员段子

程序员段子

乐一乐

皇上太拼了......  被电视剧蒙骗了好多年,三观毁灭!  这是传说中“帅气的王爷”与“美腻的王妃”,像不像屠夫...

足够自信的程序猿自信是通往成功路上的指明灯,自信的程序员更是加班夜里的探照灯,总能让BUG无所遁形。效率高的程序猿...

最近这段时间,小编絮絮叨叨说了很多严肃的东西,今天说点好玩的,轻松一下。娱乐圈有潜规则,小编认为IT圈一样,也有潜...

今天来说说一位女青年的老公以及他们的事儿。如有雷同,纯属巧合。十一年前我和程序猿第一次见面,还是大一军训期间。我甚...

公司高层公司副总A:咱们开个会研究一下这个事情怎么处理。公司副总B:如果老板没有救成功,下任是谁呢?会不会影响公司...

Web-第九天 ServletContext&Response&request学习【悟空教程】
发表时间:2018-11-07 16:30来源:Java帮帮-微信公众号

ServletContext & Response

今日内容介绍

  • 案例一:记录网站的登录成功的人数

  • 案例二:文件下载

  • 案例三:点击切换验证码

今日内容学习目标

  • 掌握ServletContext对象的使用

  • 使用response成功发送中文数据

  • 使用response可以设置响应头

第1章 案例:记录网站的登录成功的人数.

1.1 案例需求:

登录成功后,5秒后跳转到某个页面,在页面中显示您是第x位登录成功的用户.


1.2 相关知识点:

1.2.1 ServletContext

服务器启动的时候,为每个WEB应用创建一个单独的ServletContext对象,我们可以使用这个对象存取数据,用这个对象存取的数据可以在整个WEB应用中获得。可以使用如下方法存取数据

方法名

描述

setAttribute(String name,Object object)

ServletContext中存数据

getAttribute(String name)

ServletContext中取数据

removeAttribute(name)

ServletContext中移除数据

1.3 案例分析:


1.4 代码实现:

/**

* 登录代码的Servlet

*/

public class UserCountServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override

public void init() throws ServletException {

// 初始化一个变量count的值为0.

int count = 0;

// 将这个值存入到ServletContext.

this.getServletContext().setAttribute("count", count);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {

response.setContentType("text/html;charset=UTF-8");

// 1.接收表单提交的参数.

String username = request.getParameter("username");

String password = request.getParameter("password");

// 2.封装到实体对象中.

User user = new User();

user.setUsername(username);

user.setPassword(password);

// 3.调用业务层处理数据.

UserService userService = new UserService();

User existUser = userService.login(user);

// 4.根据处理结果显示信息(页面跳转).

if(existUser == null){

// 登录失败

response.getWriter().println("<h1>登录失败:用户名或密码错误!</h1>");

}else{

// 登录成功

// 记录次数:

int count = (int) this.getServletContext().getAttribute("count");

count++;

this.getServletContext().setAttribute("count", count);

response.getWriter().println("<h1>登录成功:您好:"+existUser.getNickname()+"</h1>");

response.getWriter().println("<h3>页面将在5秒后跳转!</h3>");

response.setHeader("Refresh", "5;url=/day09/CountServlet");

}

} catch (Exception e) {

e.printStackTrace();

}

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


public class CountServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 获得Count的值。

response.setContentType("text/html;charset=UTF-8");

int count = (int) this.getServletContext().getAttribute("count");

response.getWriter().println("<h1>您是第"+count+"位登录成功的用户!</h1>");

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}

1.5 总结:

1.5.1 ServletContext:读取WEB工程下的文件

ServletContext对象,tomcat为每一个web项目单独创建的一个上下文(知上知下贯穿全文)对象。就有功能:

1.可以在多个servlet之间共享数据

存放:setAttribute()

获得:getAttribute()

删除:removeAttribute()

2.可以获得当前WEB项目中的指定资源(文件)

3.可以进行整个web项目初始化数据设置

在web.xml可以给整个web项目配置初始化参数

<!-- 全局初始化参数(整个项目) -->

<context-param>

<param-name>参数名</param-name>

<param-value>参数值</param-value>

</context-param>

在实际开发中,有时候可能会需要读取Web应用中的一些资源文件,比如配置文件,图片等。为此,在ServletContext接口中定义了一些读取Web资源的方法,这些方法是依靠Servlet容器来实现的。Servlet容器根据资源文件名相对于Web应用的路径,返回关联资源文件的IO流、资源文件在文件系统的绝对路径等。

方法说明

功能描述

Set getResourcePaths(String path)

返回一个Set集合,集合中包含资源目录中子目录和文件的路径名称。参数path必须以正斜线(/)开始,指定匹配资源的部分路径

String getRealPath(String path)

返回资源文件在服务器文件系统上的真实路径(文件的绝对路径)。参数path代表资源文件的虚拟路径,它应该以正斜线开始(/)开始,“/”表示当前Web应用的根目录,如果Servlet容器不能将虚拟路径转换为文件系统的真实路径,则返回null

URL getResource(String path)

返回映射到某个资源文件的URL对象。参数path必须以正斜线(/)开始,“/”表示当前Web应用的根目录

InputStream getResourceAsStream(String path)

返回映射到某个资源文件的InputStream输入流对象。参数path传递规则和getResource()方法完全一致

了解了ServletContext接口中用于获得Web资源路径的方法后,接下来通过一个案例,分步骤演示如何使用ServletContext对象读取资源文件


1.5.2 代码实现

  • 加载配置文件

package cn.com.javahelp.servlet;

import java.io.*;

import java.util.Properties;

import javax.servlet.*;

import javax.servlet.http.*;

public class ReadFileServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 ServletContext context = this.getServletContext();

 PrintWriter out = response.getWriter();

       //获取相对路径中的输入流对象

 InputStream in = context.getResourceAsStream("/WEB-INF/classes/javahelp.properties");

 Properties pros = new Properties();

 pros.load(in);

 out.println("Company=" + pros.getProperty("Company") + "<br>");

 out.println("Address=" + pros.getProperty("Address") + "<br>");

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 this.doGet(request, response);

}

}

  • 开发者需要获取的是资源的绝对路径。接下来,通过使用getRealPath(String path)方法获取资源文件的绝对路径。

package cn.com.javahelp.servlet;

import java.io.*;

import java.util.Properties;

import javax.servlet.*;

import javax.servlet.http.*;

public class ReadFileServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 PrintWriter out = response.getWriter();

  ServletContext context = this.getServletContext();

  //获取文件绝对路径

 String path = context

                        .getRealPath("/WEB-INF/classes/javahelp.properties");

 FileInputStream in = new FileInputStream(path);

 Properties pros = new Properties();

 pros.load(in);

 out.println("Company=" + pros.getProperty("Company") + "<br>");

 out.println("Address=" + pros.getProperty("Address") + "<br>");

}

public void doPost(HttpServletRequest request,

 HttpServletResponse response)throws ServletException, IOException {

 this.doGet(request, response);

}

}

1.5.3 初始化参数

  • servlet的初始化参数

通过ServletConfig对象获得

  • 整个项目的初始化参数

通过ServletContext对象获得

1.5.4 扩展:类加载器读取文件

public static void readFile() throws IOException{

// 使用类的加载器来读取文件.

// 类的加载器用来加载class文件,class文件加载到内存.

InputStream is = ReadFileUtils.class.getClassLoader().getResourceAsStream("db.properties");

Properties properties = new Properties();

properties.load(is);

String driverClass = properties.getProperty("driverClass");

String url = properties.getProperty("url");

String username = properties.getProperty("username");

String password = properties.getProperty("password");

System.out.println(driverClass);

System.out.println(url);

System.out.println(username);

System.out.println(password);

}

第2章 案例:完成文件的下载.

2.1 需求:

在登录成功后,页面跳转到文件下载的列表的页面,点击列表中的某些链接,下载文件.


2.2 相关知识点:

2.2.1 HttpServletResponse对象

Servlet API中,定义了一个HttpServletResponse接口,它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为状态行、响应消息头、消息体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应消息头、响应消息体的方法,接下来,本节将针对这些方法进行详细的讲解。


2.2.1.1 发送状态码相关的方法

当Servlet向客户端回送响应消息时,需要在响应消息中设置状态码。为此,在HttpServletResponse接口中,定义了两个发送状态码的方法,具体如下。

1).setStatus(int status)方法

该方法用于设置HTTP响应消息的状态码,并生成响应状态行。由于响应状态行中的状态描述信息直接与状态码相关,而HTTP版本由服务器确定,因此,只要通过setStatus(int status)方法设置了状态码,即可实现状态行的发送。需要注意的是,正常情况下,Web服务器会默认产生一个状态码为200的状态行。


2).sendError(int sc)方法

该方法用于发送表示错误信息的状态码,例如,404状态码表示找不到客户端请求的资源。在response对象中,提供了两个重载的sendError(int sc)方法,具体如下:

public void sendError(int code) throws java.io.IOException

public void sendError(int code, String message) throws java.io.IOException

在上面重载的两个方法中,第一个方法只是发送错误信息的状态码,而第二个方法除了发送状态码外,还可以增加一条用于提示说明的文本信息,该文本信息将出现在发送给客户端的正文内容中。


2.2.1.2 发送响应消息头相关的方法

当Servlet向客户端发送响应消息时,由于HTTP协议的响应头字段有很多种,为此,在HttpServletResponse接口中,定义了一系列设置HTTP响应头字段的方法,如表4-1所示。

表4-1 设置响应消息头字段的方法

      方法声明

功能描述

void addHeader(String name, String value)

这两个方法都是用来设置HTTP协议的响应头字段,其中,参数name用于指定响应头字段的名称,参数value用于指定响应头字段的值。不同的是,addHeader()方法可以增加同名的响应头字段,而setHeader()方法则会覆盖同名的头字段

void setHeader(String name, String value)

void addIntHeader(String name,

int value)

这两个方法专门用于设置包含整数值的响应头。避免了使用addHeader()setHeader()方法时,需要将int类型的设置值转换为String类型的麻烦

void setIntHeader(String name,

int value)

void setContentLength(int len)

该方法用于设置响应消息的实体内容的大小,单位为字节。对于HTTP协议来说,这个方法就是设置Content-Length响应头字段的值

void setContentType(String type)

该方法用于设置Servlet输出内容的MIME类型,对于HTTP议来说,就是设置Content-Type响应头字段的值。例如,如果发送到客户端的内容是jpeg格式的图像数据,就需要将响应头字段的类型设置为“image/jpeg”。需要注意的是,如果响应的内容为文本,setContentType()方法的还可以设置字符编码,如:text/html;charset=UTF-8

void setCharacterEncoding(String charset)

该方法用于设置输出内容使用的字符编码,对HTTP 协议来说,就是设置Content-Type头字段中的字符集编码部分。一般不使用。

需要注意的是,在表4-1列举的一系列方法中,addHeader()setHeader()addIntHeader()setIntHeader()方法都是用于设置各种头字段的,而setContetType() setCharacterEncoding()方法用于设置字符编码,这些设置字符编码的方法可以有效解决乱码问题。


2.2.1.3 发送响应消息体相关的方法

由于在HTTP响应消息中,大量的数据都是通过响应消息体传递的,因此,ServletResponse遵循以IO流传递大量数据的设计理念。在发送响应消息体时,定义了两个与输出流相关的方法,具体如下。

1).getOutputStream()方法

该方法所获取的字节输出流对象为ServletOutputStream类型。由于ServletOutputStream是OutputStream的子类,它可以直接输出字节数组中的二进制数据。因此,要想输出二进制格式的响应正文,就需要使用getOutputStream()方法。


2).getWriter()方法

该方法所获取的字符输出流对象为PrintWriter类型。由于PrintWriter类型的对象可以直接输出字符文本内容,因此,要想输出内容全为字符文本的网页文档,需要使用getWriter()方法。

了解了response对象发送响应消息体的两个方法后,接下来,通过一个案例来学习这两个方法的使用。

在Eclipse中创建Web项目chapter04,在项目的src目录下,新建一个名称为cn.com.javahelp. chapter04.response的包,在包中编写一个名为PrintServlet的Servlet类,该类中使用了response对象的getOutPutStream()方法获取输出流对象,如文件4-1所示。

文件4-1 PrintServlet.java

package cn.com.javahelp.chapter04.response;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class PrintServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

HttpServletResponse response)throws ServletException, IOException {

String data = "javahelp";

// 获取字节输出流对象

 OutputStream out = response.getOutputStream();

out.write(data.getBytes());// 输出信息

}

public void doPost(HttpServletRequest request,

HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}

web.xml中配置完PrintServlet的映射后,启动Tomcat服务器,在浏览器的地址栏中输入地址“http://localhost:8080/chapter04/PrintServlet”访问PrintServlet,浏览器的显示结果如图4-2所示。

图 4-2

从图4-2中可以看出,浏览器显示出了response对象响应的数据。由此可见,response对象的getOutputStream()方法可以很方便的发送响应消息体。

接下来,对文件4-1进行修改,使用getWriter()方法发送消息体,修改后的代码如文件4-2所示。

文件4-2 PrintServlet.java

package cn.com.javahelp.chapter04.response;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class PrintServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

  HttpServletResponse response)throws ServletException, IOException {

 String data = "javahelp";

 // 获取字符输出流对象

 PrintWriter print = response.getWriter();

 print.write(data); // 输出信息

}

public void doPost(HttpServletRequest request,

        HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}


重启Tomcat服务器,在浏览器的地址栏中输入地址 “http://localhost:8080/chapter04/PrintServlet”再次访问PrintServlet,浏览器的显示结果同样如图4-2所示。

注意:

虽然response对象的getOutputStream()和getWriter()方法都可以发送响应消息体,但是,它们之间互相排斥,不可同时使用,否则会发生IllegalStateException异常,如图4-3所示。

图4-3

4-3中发生异常的原因就是在Servlet中,调用response.getWriter() 方法之前已经调用了response.getOutputStream() 方法。


2.2.2 文件下载原理

对于文件下载,相信读者并不会陌生,因为通常在上网时所下的图片、文档和影片等都是文件下载的范畴。现在很多网站都提供了下载各类资源的功能,因此在学习Web开发过程中,有必要学习文件下载的实现方式。

实现文件下载功能比较简单,通常情况下,不需要使用第三方组件实现,而是直接使用Servlet类和输入/输出流实现。与访问服务器文件不同的是,要实现文件的下载,不仅需要指定文件的路径,还需要在HTTP协议中设置两个响应消息头,具体如下:

//设定接收程序处理数据的方式

Content-Disposition: attachment; filename =

//设定实体内容的MIME类型

Content-Typeapplication/x-msdownload

浏览器通常会直接处理响应的实体内容,需要在HTTP响应消息中设置两个响应消息头字段,用来指定接收程序处理数据内容的方式为下载方式。当单击“下载”超链接时,系统将请求提交到对应的Servlet。在该Servlet中,首先获取下载文件的地址,并根据该地址创建文件字节输入流,然后通过该流读取下载文件内容,最后将读取的内容通过输出流写到目标文件中。


2.3 代码实现

public class DownloadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 1.接收参数

String filename = request.getParameter("filename");

// 2.完成文件下载:

// 2.1设置Content-Type

String type = this.getServletContext().getMimeType(filename);

response.setHeader("Content-Type", type);

// 2.2设置Content-Disposition

response.setHeader("Content-Disposition", "attachment;filename="+filename);

// 2.3设置文件的InputStream.

String realPath = this.getServletContext().getRealPath("/download/"+filename);

InputStream is = new FileInputStream(realPath);

// 获得response的输出流:

OutputStream os = response.getOutputStream();

int len = 0;

byte[] b = new byte[1024];

while((len = is.read(b))!= -1){

os.write(b, 0, len);

}

is.close();

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


2.4 总结:

2.4.1 中文文件的下载:

  • Firefox浏览器下载中文文件的时候采用的是Base64的编码.

  • IE浏览器,谷歌浏览器等,下载中文文件的时候采用的URL的编码.

  • 可以使用工具类“DownloadUtils.java”进行处理

/**

* 文件下载的Servlet

*/

public class DownloadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 1.接收参数

String filename = new String(request.getParameter("filename"));

System.out.println(filename);

// 2.完成文件下载:

// 2.1设置Content-Type

String type = this.getServletContext().getMimeType(filename);

response.setHeader("Content-Type", type);

// 2.3设置文件的InputStream.

String realPath = this.getServletContext().getRealPath("/download/"+filename);

// 根据浏览器的类型处理中文文件的乱码问题:

String agent = request.getHeader("User-Agent");

System.out.println(agent);

if(agent.contains("Firefox")){

filename = base64EncodeFileName(filename);

}else{

filename = URLEncoder.encode(filename,"UTF-8");

}


// 2.2设置Content-Disposition

response.setHeader("Content-Disposition", "attachment;filename="+filename);

InputStream is = new FileInputStream(realPath);

// 获得response的输出流:

OutputStream os = response.getOutputStream();

int len = 0;

byte[] b = new byte[1024];

while((len = is.read(b))!= -1){

os.write(b, 0, len);

}

is.close();

}

public static String base64EncodeFileName(String fileName) {

BASE64Encoder base64Encoder = new BASE64Encoder();

try {

return "=?UTF-8?B?"

+ new String(base64Encoder.encode(fileName

.getBytes("UTF-8"))) + "?=";

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

throw new RuntimeException(e);

}

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


2.4.2 response解决中文输出乱码问题

在开发中,如果需要响应给浏览器中文数据,我们需要使用getWriter()方法,tomcat使用getWriter()处理字符时,默认编码时ISO8859-1。响应给浏览器的数据,此时为乱码。在开发中我们需要使用setCharacterEncoding方法来设置编码。此时响应给浏览器的数据为正常数据,但浏览器查看编码,可能使我们查看到内容仍为乱码,需要使用响应头 content-type进行设置。

在HttpServletResponse对象中,提供了两种解决乱码的方案,具体如下:

  • 第一种方式:

// 设置HttpServletResponse使用utf-8编码,此数据提供给tomcat使用。

response.setCharacterEncoding("utf-8");

// 通知浏览器查看编码为utf-8

response.setHeader("Content-Type","text/html;charset=utf-8");

  • 第二种方式:

// 使用通用方法,setContentType底层自动调用了setCharacterEncoding

response.setContentType("text/html;charset=utf-8");

通常情况下,为了使代码更加简洁,会采用第二种方式。实现代码如下文所示:

package cn.com.javahelp.chapter04.response;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class ChineseServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

      HttpServletResponse response) throws ServletException, IOException {

        //设置字符编码

         response.setContentType("text/html;charset=utf-8");

 String data="中国";

 PrintWriter out = response.getWriter();

 out.println(data);

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response) throws ServletException, IOException {

 doGet(request,response);

}

}

启动Tomcat服务器,在浏览器的地址栏中输入地址“http://localhost:8080/day14/ChineseServlet”访问ChineseServlet,浏览器显示出了正确的中文字符,如下图所示。


第3章 案例:点击切换验证码

3.1 需求:

在访问登录页面时,需要生产验证码。从而防止用户使用程序恶意登录。


3.2 分析

3.3 代码实现

  • 步骤1:修改login.jsp页面,确定验证码图片显示的位置

  • 步骤2:拷贝VerifyCodeServlet实现类

public class VerifyCodeServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

//生成图片

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//1 高和宽

int height = 30;

int width = 60;

String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";

Random random = new Random();

//2 创建一个图片

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

//3 获得画板

Graphics g = image.getGraphics();

//4 填充一个矩形

// * 设置颜色

g.setColor(Color.BLACK);

g.fillRect(0, 0, width, height);

g.setColor(Color.WHITE);

g.fillRect(1, 1, width-2, height-2);

// * 设置字体

g.setFont(new Font("宋体", Font.BOLD|Font.ITALIC, 25));

//5 写随机字

for(int i = 0 ; i < 4 ; i ++){

// 设置颜色--随机数

g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));

// 获得随机字

int index = random.nextInt(data.length());

String str = data.substring(index, index + 1);

// 写入

g.drawString(str, width/6 * (i + 1), 20);

}

//6 干扰线

for(int i = 0 ; i < 3 ; i ++){

// 设置颜色--随机数

g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));

// 随机绘制先

g.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width),random.nextInt(height));

// 随机点

g.drawOval(random.nextInt(width), random.nextInt(height), 2, 2);

}

//end 将图片响应给浏览器

ImageIO.write(image, "jpg", response.getOutputStream());

}

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

  • 步骤3:编写web.xml文件

<servlet>

<servlet-name>VerifyCodeServlet</servlet-name>

<servlet-class>cn.com.javahelp.web.servlet.VerifyCodeServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>VerifyCodeServlet</servlet-name>

<url-pattern>/VerifyCodeServlet</url-pattern>

</servlet-mapping>

3.4 点击切换

  • 添加点击图片或文字,更换验证码

  • 代码实现,点击图片或文件,都可以更换验证码图片。

<div>

<img id="imgId" onclick="changeImg()" style="cursor: pointer;"

src="${pageContext.request.contextPath}/VerifyCodeServlet"/>  

<a href="javascript:void(0)" onclick="changeImg()" >换一张</a>

<script type="text/javascript">

function changeImg(){

$("#imgId").attr("src","${pageContext.request.contextPath}/VerifyCodeServlet?t="+new Date());

}

</script>

</div>

request

今日内容介绍

  • 案例一:完成用户注册的案例

  • 案例二:完善用户登录,显示错误信息

今日内容学习目标

  • 使用request获得表单请求参数

  • 使用request完成请求转发,并成功在一次请求中传递数据

第4章 案例:完成用户注册的功能:

4.1 需求:

网站首页上点击注册的链接,跳转到注册页面,在注册页面中输入信息.完成注册:(将数据保存到数据库中).


4.2 相关知识点:

4.2.1 HttpServletRequest对象

Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求消息头和请求消息体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法,接下来,本节将针对这些方法进行详细地讲解。


4.2.1.1 获取请求行信息的相关方法

当访问Servlet时,会在请求消息的请求行中,包含请求方法、请求资源名、请求路径等信息,为了获取这些信息,在HttpServletRequest接口中,定义了一系列用于获取请求行的方法,如表4-2所示。

表4-2 获取请求行的相关方法

方法声明

功能描述

String getMethod()

该方法用于获取HTTP请求消息中的请求方式(如GET、POST等)

String getRequestURI()

该方法用于获取请求行中资源名称部分,即位于URL的主机和端口之后、参数部分之前的部分

String getQueryString()

该方法用于获取请求行中的参数部分,也就是资源路径后面问号(?)以后的所有内容

String getProtocol()

该方法用于获取请求行中的协议名和版本,例如,HTTP/1.0或HTTP/1.1

String getContextPath()

该方法用于获取请求URL中属于WEB应用程序的路径,这个路径以“/”开头,表示相对于整个WEB站点的根目录,路径结尾不含“/”。如果请求URL属于WEB站点的根目录,那么返回结果为空字符串("")

String getServletPath()

该方法用于获取Servlet的名称或Servlet所映射的路径

String getRemoteAddr()

该方法用于获取请求客户端的IP地址,其格式类似于“192.168.0.3

String getRemoteHost()

该方法用于获取请求客户端的完整主机名,其格式类似于“pc1.javahelp.com.cn”。需要注意的是,如果无法解析出客户机的完整主机名,该方法将会返回客户端的IP地址

int getRemotePort()

该方法用于获取请求客户端网络连接的端口号

String getLocalAddr()

该方法用于获取Web服务器上接收当前请求网络连接的IP地址

String getLocalName()

该方法用于获取Web服务器上接收当前网络连接IP所对应的主机名

int getLocalPort()

该方法用于获取Web服务器上接收当前网络连接的端口号

String getServerName()

该方法用于获取当前请求所指向的主机名,即HTTP请求消息中Host头字段所对应的主机名部分

int getServerPort()

该方法用于获取当前请求所连接的服务器端口号,即如果HTTP请求消息中Host头字段所对应的端口号部分

String getScheme()

该方法用于获取请求的协议名,例如httphttpsftp

StringBuffer getRequestURL()

该方法用于获取客户端发出请求时的完整URL,包括协议、服务器名、端口号、资源路径等信息,但不包括后面的查询参数部分。注意,getRequestURL()方法返回的结果是StringBuffer类型,而不是String类型,这样更便于对结果进行修改

在表4-2中,列出了一系列用于获取请求消息行信息的方法,为了使读者更好地理解这些方法,接下来,通过一个案例来演示这些方法的使用。

在chapter04项目的src目录下,新建一个名称为cn.com.javahelp.request的包,在包中编写一个名为RequestLineServlet的类,该类中编写了用于获取请求行中相关信息的方法,如文件4-3所示。

文件4-3  RequestLineServlet.java

package cn.com.javahelp.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestLineServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

 PrintWriter out = response.getWriter();

 // 获取请求行的相关信息

 out.println("getMethod : " + request.getMethod() + "<br>");

 out.println("getRequestURI : " + request.getRequestURI() + "<br>");

 out.println("getQueryString:"+request.getQueryString() + "<br>");

 out.println("getProtocol : " + request.getProtocol() + "<br>");

 out.println("getContextPath:"+request.getContextPath() + "<br>");

 out.println("getPathInfo : " + request.getPathInfo() + "<br>");

 out.println("getPathTranslated : "

                      + request.getPathTranslated() + "<br>");

 out.println("getServletPath:"+request.getServletPath() + "<br>");

 out.println("getRemoteAddr : " + request.getRemoteAddr() + "<br>");

 out.println("getRemoteHost : " + request.getRemoteHost() + "<br>");

 out.println("getRemotePort : " + request.getRemotePort() + "<br>");

 out.println("getLocalAddr : " + request.getLocalAddr() + "<br>");

 out.println("getLocalName : " + request.getLocalName() + "<br>");

 out.println("getLocalPort : " + request.getLocalPort() + "<br>");

 out.println("getServerName : " + request.getServerName() + "<br>");

 out.println("getServerPort : " + request.getServerPort() + "<br>");

 out.println("getScheme : " + request.getScheme() + "<br>");

 out.println("getRequestURL : " + request.getRequestURL() + "<br>");

}

public void doPost(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

web.xml中配置完RequestLineServlet的映射后,启动Tomcat服务器,在浏览器的地址栏中输入地址

http://localhost:8080/ day15/RequestLineServlet”访问RequestLineServlet,浏览器的显示结果如图4-14所示。

图4-14

从图4-14中可以看出,浏览器显示出了请求RequestLineServlet时,发送的请求行信息。由此可见,通过HttpServletRequest对象可以很方便的获取到请求行的相关信息。


4.2.1.2 获取请求消息头的相关方法

当请求Servlet时,需要通过请求头向服务器传递附加信息,例如,客户端可以接收的数据类型,压缩方式,语言等等。为此,在HttpServletRequest接口中,定义了一系列用于获取HTTP请求头字段的方法,如表4-3所示。

表4-3 获取请求消息头的方法

方法声明

功能描述

String getHeader(String name)

该方法用于获取一个指定头字段的值,如果请求消息中没有包含指定的头字段,getHeader()方法返回null;如果请求消息中包含有多个指定名称的头字段,getHeader()方法返回其中第一个头字段的值

Enumeration getHeaders(String name)

该方法返回一个Enumeration集合对象,该集合对象由请求消息中出现的某个指定名称的所有头字段值组成。在多数情况下,一个头字段名在请求消息中只出现一次,但有时候可能会出现多次

Enumeration getHeaderNames()

该方法用于获取一个包含所有请求头字段的Enumeration对象

int getIntHeader(String name)

该方法用于获取指定名称的头字段,并且将其值转为int类型。需要注意的是,如果指定名称的头字段不存在,返回值为-1;如果获取到的头字段的值不能转为int类型,将发生NumberFormatException异常

Long getDateHeader(String name)

该方法用于获取指定头字段的值,并将其按GMT时间格式转换成一个代表日期/时间的长整数,这个长整数是自197011000秒算起的以毫秒为单位的时间值

在表4-3中,列出了一系列用于读取HTTP请求消息头字段的方法,为了更好地掌握这些方法,接下来通过一个案例来学习这些方法的使用。

在cn.com.javahelp.request包中编写一个名为RequestHeadersServlet的类,该类使用getHeaderNames()方法来获取请求消息头信息,如文件4-4所示。

文件4-4 RequestHeadersServlet.java

package cn.com.javahelp.request;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Enumeration;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestHeadersServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

        HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

 PrintWriter out = response.getWriter();

         // 获取请求消息中所有头字段

 Enumeration headerNames = request.getHeaderNames();

 // 使用循环遍历所有请求头,并通过getHeader()方法获取一个指定名称的头字段

 while (headerNames.hasMoreElements()) {

 String headerName = (String) headerNames.nextElement();

 out.print(headerName + " : "

                       + request.getHeader(headerName)+ "<br>");

 }

}

public void doPost(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

web.xml中配置完RequestHeadersServlet映射后,启动Tomcat服务器,在浏览器的地址栏中输入地址“http://localhost:8080/day15/RequestHeadersServlet”访问RequestHeadersServlet,浏览器的显示结果如图4-15所示。


4.2.1.3 获取请求参数

在实际开发中,经常需要获取用户提交的表单数据,例如,用户名、密码、电子邮件等,为了方便获取表单中的请求参数,在HttpServletRequest接口中,定义了一系列获取请求参数的方法,如表4-4所示。

表4-4 获取请求参数的方法

      方法声明

功能描述

String getParameter(String name)

该方法用于获取某个指定名称的参数值,如果请求消息中没有包含指定名称的参数,getParameter()方法返回null;如果指定名称的参数存在但没有设置值,则返回一个空串;如果请求消息中包含有多个该指定名称的参数,getParameter()方法返回第一个出现的参数值

String[] getParameterValues(String name)

HTTP请求消息中可以有多个相同名称的参数(通常由一个包含有多个同名的字段元素的FORM表单生成),如果要获得HTTP请求消息中的同一个参数名所对应的所有参数值,那么就应该使用getParameterValues()方法,该方法用于返回一个String类型的数组

Enumeration getParameterNames()

getParameterNames()方法用于返回一个包含请求消息中所有参数名的Enumeration对象,在此基础上,可以对请求消息中的所有参数进行遍历处理

Map getParameterMap()

getParameterMap()方法用于将请求消息中的所有参数名和值装入进一个Map对象中返回

4-4中,列出了HttpServletRequest获取请求参数的一系列方法。其中,getParameter()方法用于获取某个指定的参数,而getParameterValues()方法用于获取多个同名的参数。接下来,通过一个具体的案例,分步骤讲解这两个方法的使用,具体如下:


1)在day15项目的WebContent根目录下编写一个表单文件form.html,如文件4-5所示。

文件4-5 form.html

<!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>

<form action="/day15/RequestParamsServlet" method="POST">

用户名:<input type="text" name="username"><br>

  码:<input type="password" name="password">

     <br>

爱好:

<input type="checkbox" name="hobby" value="sing">唱歌

<input type="checkbox" name="hobby" value="dance">跳舞

<input type="checkbox" name="hobby" value="football">足球<br>

<input type="submit" value="提交">

</form>

</body>

</html>


2)在cn.com.javahelp.request包中编写一个名称为RequestParamsServletServlet类,使用该Servlet获取请求参数,如文件4-6所示。

文件4-6 RequestParamsServlet.java

package cn.com.javahelp.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestParamsServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 String name = request.getParameter("username");

String password = request.getParameter("password");

 System.out.println("用户名:" + name);

 System.out.println("密  码:" + password);

 // 获取参数名为“hobby”的值

 String[] hobbys = request.getParameterValues("hobby");

 System.out.print("爱好:");

for (int i = 0; i < hobbys.length; i++) {

 System.out.print(hobbys[i] + ", ");

 }

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

在文件4-14中,由于参数名为“hobby”的值可能有多个,因此,需要使用getParameterValues()方法,获取多个同名参数的值,返回一个String类型的数组,通过遍历数组,打印出每个“hobby”参数对应的值。


3)在web.xml中配置完RequestParamsServlet映射后,启动Tomcat服务器,在浏览器的地址栏中输入地址“http://localhost:8080/ day15/form.html”访问form.html页面,并填写表单相关信息,填写后的页面如图4-18所示。

图4-18 运行结果

4)单击图4-18所示的“提交”按钮,在Eclipse的控制台打印出了每个参数的信息,如图4-19所示。

图4-19 运行结果

4.2.2 BeanUtils

BeanUtils 是 Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作。它可以给JavaBean封装一个字符串数据,也可以将一个表单提交的所有数据封装到JavaBean中。

使用第三方工具,需要导入jar包:

BeanUtils工具常用工具类有两个:BeanUtilsConvertUtilsBeanUtils用于封装数据,ConvertUtils用于处理类型转换,常用API如下


方法

描述

BeanUtils对象

populate(Object bean, Map<String,String[]> properties)  

Map数据封装到指定Javabean中,一般用于将表单的所有数据封装到javabean




ConvertUtils

register(Converter converter, Class clazz)

注册类型转换器

  • 提供JavaBean User ,并提供对应的构造方法

public class User {

private String username;

private String password;

private int age;

private Date birthday;

public User() {

}


public User(String username, String password, int age, Date birthday) {

...

}

  • 功能1:封装表单数据,使用Map 模拟 request.getParameterMap()

@Test

public void demo01() throws Exception{

//map数据封装给user

Map<String,String[]> properties = new HashMap<String,String[]>();

properties.put("username", new String[]{"jack"});

properties.put("password", new String[]{"1234"});

properties.put("age", new String[]{"18"});

User user = new User();

// 封装数据

BeanUtils.populate(user, properties);

System.out.println(user);

}

  • 功能2:封装表单数据,使用类型转换器

@Test

public void demo02() throws Exception{

//转换器

Map<String,String[]> properties = new HashMap<String,String[]>();

properties.put("username", new String[]{"jack"});

properties.put("password", new String[]{"1234"});

properties.put("age", new String[]{"18"});

properties.put("birthday", new String[]{"2012-12-21"});

User user = new User();

//1 创建BeanUtils提供时间转换器

DateConverter dateConverter = new DateConverter();

//2 设置需要转换的格式

dateConverter.setPattern("yyyy-MM-dd");

//3 注册转换器

ConvertUtils.register(dateConverter, java.util.Date.class);

//4 封装数据

BeanUtils.populate(user, properties);

System.out.println(user);

}

  • 功能3:编写工具类

public class MyBeanUtils {

/**

* 将数据封装给JavaBean,支持时间类型转换

* @param user

* @param properties

*/

public static void populate(Object user, Map<String,String[]> properties){

try {

//1 创建BeanUtils提供时间转换器

DateConverter dateConverter = new DateConverter();

//2 设置需要转换的格式

dateConverter.setPatterns(new String[]{"yyyy-MM-dd","yyyy-MM-dd HH:mm:ss"});

//3 注册转换器

ConvertUtils.register(dateConverter, java.util.Date.class);

//4 封装数据

BeanUtils.populate(user, properties);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

使用

  • 工具类:高级(了解)

/**

* 高级封装,不需要new javabean

* @param beanClass

* @param properties

* @return

*/

public static <T> T populate(Class<T> beanClass, Map<String,String[]> properties){

try {

//1 使用反射创建实例

T bean = beanClass.newInstance();

//2.1 创建BeanUtils提供时间转换器

DateConverter dateConverter = new DateConverter();

//2.2 设置需要转换的格式

dateConverter.setPatterns(new String[]{"yyyy-MM-dd","yyyy-MM-dd HH:mm:ss"});

//2.3 注册转换器

ConvertUtils.register(dateConverter, java.util.Date.class);


//3 封装数据

BeanUtils.populate(bean, properties);

return bean;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

使用

4.3 代码实现

package cn.com.javahelp.e_regist;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* 用户注册的Servlet

*/

public class RegistServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try{

request.setCharacterEncoding("UTF-8");

// 1.接收参数

String username = request.getParameter("username");

String password = request.getParameter("password");

String email = request.getParameter("email");

String name = request.getParameter("name");

String sex = request.getParameter("sex");

String telephone = request.getParameter("telephone");

// 2.封装数据

User user = new User();

user.setUsername(username);

user.setPassword(password);

user.setEmail(email);

user.setName(name);

user.setSex(sex);

user.setTelephone(telephone);


// 3.调用业务层处理数据

UserService userService = new UserService();

userService.regist(user);

// 4.页面跳转

response.sendRedirect(request.getContextPath()+"/login.htm");

}catch(Exception e){

e.printStackTrace();

}

}


protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


4.4 总结:

4.4.1 解决请求参数的中文乱码问题

在填写表单数据时,难免会输入中文,如姓名、公司名称等。在文件4-13中,由于HTML设置了浏览器在传递请求参数时,采用的编码方式是UTF-8,但在解码时采用的是默认的ISO8859-1,因此会导致乱码的出现。在浏览器的地址栏中输入地址“http://localhost:8080/day15/form.html”再次访问form.html页面,输入用户名为“悟空教程”以及相关表单信息,如图4-20所示。

图4-20 运行结果

单击图4-20中的“提交”按钮,这时,控制台打印出了每个参数的值,具体如图4-21所示。

图4-21 运行结果

从图4-21可以看出,当输入的用户名为中文时,出现了乱码问题。通过本任务的学习,读者将学会如何处理请求参数的中文乱码问题。


【实现步骤】

1.设置编码方式

HttpServletRequest接口中,提供了一个setCharacterEncoding()方法,该方法用于设置request对象的解码方式,接下来,对文件4-6进行修改,修改后的代码如文件4-7所示。该方法用于返回请求消息的实体部分的字符集编码

文件4-7 RequestParamsServlet.java

package cn.com.javahelp.chapter04.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestParamsServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

//设置request对象的解码方式

        request.setCharacterEncoding("utf-8");

String name = request.getParameter("username");

String password = request.getParameter("password");

System.out.println("用户名:" + name);

System.out.println("密   码:" + password);

String[] hobbys = request.getParameterValues("hobby");

System.out.print("爱好:");

for (int i = 0; i < hobbys.length; i++) {

System.out.print(hobbys[i] + ", ");

}

}

public void doPost(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}


2.查看运行结果

启动Tomcat服务器,再次访问form.html页面,输入中文用户名“悟空教程”以及相关表单信息,控制台打印的结果如图4-22所示。

图4-22 运行结果

从图4-22可以看出,控制台输出的参数信息没有出现乱码。需要注意的是,这种解决乱码的方式只对POST方式有效,而对GET方式无效。为了验证GET方式的演示效果,接下来,将form.html文件中method属性的值改为“GET”。重新访问form.html页面并填写中文信息,控制台的打印结果如图4-23所示。

图4-23 运行结果

从图4-23中可以看出,使用GET方式提交表单,用户名出现了乱码,这就验证了setCharacterEncoding()方法只对POST提交方式有效的结论。为了解决GET方式提交表单时出现的中文乱码问题,接下来,对文件4-7进行修改,增加一行代码,如下所示:

name=new String(name.getBytes("iso8859-1"),"utf-8");

重启Tomcat服务器,再次访问form.html网页,输入中文用户名“悟空教程”,这时,控制台的打印结果没有出现乱码,如图4-24所示。

图4-24

总结

第5章 案例:登录错误提示信息

5.1 需求

完善“登录”案例,如果用户填写信息有误,在当前表单页面中给出相应的提示。


5.2 相关知识点

5.2.1 通过Request对象传递数据

Request对象不仅可以获取一系列数据,还可以通过属性传递数据。在ServletRequest接口中,定义了一系列操作属性的方法,具体如下:

  • setAttribute()方法

该方法用于将一个对象与一个名称关联后存储进ServletRequest对象中,其完整语法定义如下:

public void setAttribute(java.lang.String name,java.lang.Object o);

需要注意的是,如果ServletRequest对象中已经存在指定名称的属性,setAttribute()方法将会先删除原来的属性,然后再添加新的属性。如果传递给setAttribute()方法的属性值对象为null,则删除指定名称的属性,这时的效果等同于removeAttribute()方法。

  • getAttribute()方法

该方法用于从ServletRequest对象中返回指定名称的属性对象,其完整的语法定义如下:

public java.lang.String getAttribute (java.lang.String name);

  • removeAttribute()方法

该方法用于从ServletRequest对象中删除指定名称的属性,其完整的语法定义如下:

public void removeAttribute(java.lang.String name);

  • getAttributeNames()方法

该方法用于返回一个包含ServletRequest对象中的所有属性名的Enumeration对象,在此基础上,可以对ServletRequest对象中的所有属性进行遍历处理。getAttributeNames()方法的完整语法定义如下:

public java.util.Enumeration getAttributeNames();

需要注意的是,只有属于同一个请求中的数据才可以通过ServletRequest对象传递数据。关于ServletRequest对象操作属性的具体用法,将在后面的小节进行详细讲解。在此,大家只需了解即可。


5.2.2 RequestDispatcher对象的应用

5.2.2.1 RequestDispatcher接口

当一个Web资源收到客户端的请求后,如果希望服务器通知另外一个资源去处理请求,这时,除了使用sendRedirect()方法实现请求重定向外,还可以通过RequestDispatcher接口的实例对象来实现。在ServletRequest接口中定义了一个获取RequestDispatcher对象的方法,如表4-5所示。

表4-5  获取RequestDispatcher对象的方法

方法声明

功能描述

getRequestDispatcher(String path)

返回封装了某个路径所指定资源的RequestDispatcher对象。其中,参数path须以 “/开头,用于表示当前Web应用的根目录。需要注意的是,WEB-INF目录中的内容对RequestDispatcher对象也是可见的,因此,传递给getRequestDispatcher(String path)方法的资源可以是WEB-INF目录中的文件

获取到RequestDispatcher对象后,最重要的工作就是通知其它Web资源处理当前的Servlet请求,为此,在RequestDispatcher接口中,定义了两个相关方法,如表4-6所示。

表4-6 RequestDispatcher接口的方法

方法声明

功能描述

forward(ServletRequest request,

ServletResponse response)

该方法用于将请求从一个Servlet传递给另外的一个Web资源。在Servlet中,可以对请求做一个初步处理,然后通过调用这个方法,将请求传递给其它资源进行响应。需要注意的是,该方法必须在响应提交给客户端之前被调用,否则将抛出IllegalStateException异常

include(ServletRequest request,

ServletResponse response)

该方法用于将其它的资源作为当前响应内容包含进来

4-6列举的两个方法中,forward()方法可以实现请求转发,include()方法可以实现请求包含,关于请求转发相关知识,将在下面的小节中进行详细讲解。

请求包含:就是将当前页面和被包含页面的输出结果,合并一起响应给浏览器。


5.2.2.2 请求转发

Servlet中,如果当前Web资源不想处理请求时,可以通过forward()方法将当前请求传递给其它的Web资源进行处理,这种方式称为请求转发。为了使读者更好地理解使用forward()方法实现请求转发的工作原理,接下来通过一张图来描述,如图4-25所示。

图4-25 forward()方法的工作原理

从图4-25中可以看出,当客户端访问Servlet1时,可以通过forward()方法将请求转发给其它Web资源,其它Web资源处理完请求后,直接将响应结果返回到客户端。

了解了forward()方法的工作原理后,接下来,通过一个案例来学习forward()方法的使用。

day15项目的cn.com.javahelp.request包中编写一个名为RequestForwardServletServlet类,该类使用forword()方法将请求转发到一个新的Servlet页面,如文件4-8所示。


文件4-8 RequestForwardServlet.java

package cn.com.javahelp.request;

import java.io.IOException;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestForwardServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

 // 将数据存储到request对象中

 request.setAttribute("company", "上海满吉教育科技有限公司");

 RequestDispatcher dispatcher = request.getRequestDispatcher("/ResultServlet");

 dispatcher.forward(request, response);

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}

通过使用forward()方法,将当前Servlet的请求转发到ResultServlet页面,在cn.com.javahelp.request包中编写一个名为ResultServletServlet类,该类用于获取RequestForwardServlet类中保存在request对象中的数据并输出,ResultServlet类的代码实现。


ResultServlet.java

package cn.com.javahelp.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class ResultServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

          // 获取PrintWriter对象用于输出信息

 PrintWriter out = response.getWriter();

 // 获取request请求对象中保存的数据

 String company = (String) request.getAttribute("company");

 if (company != null) {

  out.println("公司名称:" + company + "<br>");

 }

}

public void doPost(HttpServletRequest request,

    HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

web.xml中,添加完两个Servlet的映射信息后,启动Tomcat服务器,在浏览器中输入地址http://localhost:8080/day15/RequestForwardServlet”访问RequestForwardServlet,浏览器的显示结果如图4-26所示。

图4-26 运行结果

5.3 分析


5.4 实现

  • 步骤1:修改“UserLoginServlet”,登录错误时,使用request设置设置错误信息,并请求转发到“login.jsp”.

    • 1.创建“login.jsp”

    • 2.添加页面编码(固定写法)

<%@ page language="java" contentType="text/html; charset=UTF-8"

   pageEncoding="UTF-8"%>

    • 3.复制“login.html”内容到“login.jsp”

// * request作用设置错误信息

request.setAttribute("msg", "用户名或密码不匹配");

// * 请求转发到登录页面

request.getRequestDispatcher("login.jsp").forward(request, response);

  • 步骤2:在“login.jsp”页面显示错误信息(固定写法)

<%=request.getAttribute("msg") %>

5.5 总结

5.5.1 重定向和转发的区别:(redirectforward的区别)

* 1.重定向的地址栏会发生变化,转发的地址栏不变.

* 2.重定向两次请求两次响应,转发一次请求一次响应.

* 3.重定向路径需要加工程名,转发的路径不需要加工程名.

* 4.重定向可以跳转到任意网站,转发只能在服务器内部进行转发.

第6章 总结


扩展知识

第7章 案例用户注册(使用beanutils)

7.1 创建数据库导入sql语句 引入jar包  工具类 数据库配置文件

7.2 创建servlet

import java.io.IOException;

import java.io.UnsupportedEncodingException;

import java.lang.reflect.InvocationTargetException;

import java.util.Map;

import java.util.UUID;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;

import cn.com.javahelp.domain.User;

import cn.com.javahelp.service.UserService;

public class RegisterServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

request.setCharacterEncoding("UTF-8");

//1、获得请求数据

Map<String, String[]> map = request.getParameterMap();

//2、封装实体

User user = new User();


//使用BeanUtils进行映射封装(map中的key的名称与实体中的属性名称进行映射)

//bean代表要被封装实体  properties代表存储数据的map

try {

BeanUtils.populate(user, map);

} catch (IllegalAccessException | InvocationTargetException e) {

e.printStackTrace();

}

//手动封装uid

String uid = UUID.randomUUID().toString();

user.setUid(uid);


//3、传递数据到业务对象的方法

UserService service = new UserService();

//4、接受业务方法的处理结果

boolean isRegistSuccess = service.regist(user);

//5、为客户响应

if(isRegistSuccess){

//注册成功 返回登录页面

response.sendRedirect(request.getContextPath()+"/login.jsp");

}else{

response.setContentType("text/html;charset=UTF-8");

response.getWriter().write("对不起,注册失败,请联系管理员");

}

}

public void regist1(HttpServletRequest request, HttpServletResponse response)throws IOException{

request.setCharacterEncoding("UTF-8");

//1、获得请求数据

String username = request.getParameter("username");

String password = request.getParameter("password");

String email = request.getParameter("email");

String name = request.getParameter("name");

String sex = request.getParameter("sex");

String birthday = request.getParameter("birthday");

//2、封装实体

User user = new User();

//UUID:随机产生的永不不重复的字符串32

String uid = UUID.randomUUID().toString();//36java产生uuid有四个-

user.setUid(uid);

user.setUsername(username);

user.setPassword(password);

user.setEmail(email);

user.setName(name);

user.setSex(sex);

user.setBirthday(birthday);

//3、传递数据到业务对象的方法

UserService service = new UserService();

//4、接受业务方法的处理结果

boolean isRegistSuccess = service.regist(user);

//5、为客户响应

if(isRegistSuccess){

//注册成功 返回登录页面

response.sendRedirect(request.getContextPath()+"/login.jsp");

}else{

response.setContentType("text/html;charset=UTF-8");

response.getWriter().write("对不起,注册失败,请联系管理员");

}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}

以下代码编写根据servlet需求不断的迭代


7.3 修改页面name属性 及action属性


7.4 创建实体类(根据表来写)

生成set&get方法,实际开发中一般都是set不会做无参,无参只是方便教学

public class User {

/*`uid` varchar(50) NOT NULL,

 `username` varchar(20) DEFAULT NULL,

 `password` varchar(50) DEFAULT NULL,

 `name` varchar(20) DEFAULT NULL,

 `email` varchar(30) DEFAULT NULL,

 `telephone` varchar(20) DEFAULT NULL,

 `birthday` date DEFAULT NULL,

 `sex` varchar(10) DEFAULT NULL,

 `state` int(11) DEFAULT NULL,

 `code` varchar(64) DEFAULT NULL*/


private String uid;//唯一性标识 主键

private String username;//用户名

private String password;//密码

private String name;//真实姓名

private String email;//邮箱

private String telephone;

private String birthday;//生日

private String sex;//性别

private int state;

private String code;



public String getUid() {

return uid;

}

public void setUid(String uid) {

this.uid = uid;

}

public String getUsername() {

return username;