【JavaWeb学习】03:请求与响应

第三章:请求与响应


在这里插入图片描述

Servlet的主要作用是处理客户端请求,并作出一定的响应。如下图为浏览器访问Servlet的交互过程:

在这里插入图片描述

注意:

  • 在Web服务器运行阶段,每个Servlet都只会创建一个实例对象。
  • 每次HTTP请求Web服务器都会调用所请求Servlet实例的service()方法,重新创建一个request对象和一个response对象。

本章主要针对request对象和response对象进行详细的讲解

一、HttpServletResponse对象

1.发送状态码相关的方法

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

在这里插入图片描述

(1)`setStatus(int status):

用于设置HTTP响应消息的状态码,并生成响应状态行。

由于响应状态行中的状态描述信息直接与状态码相关,而HTTP版本由服务器确定;因此只要通过setStatus(int status)方法设置了状态码,即可实现状态行的发送。

注意:正常情况下,Web服务器会默认产生一个状态码为200的状态行

(2)`sendError(int sc):

用于发送错误信息的状态码。

例如,404状态码表示找不到客户端请求的资源。在response对象中提供了两个重载的sendError(int sc)方法:

//method1:只用于发送错误信息状态码
public void sendError(int code) throws java.io.IOException
//method2:只用于发送错误信息状态码,并增加一条提示说明的文本(将出现在客户端的正文内容当中)
public void sendError(int code, String message) throws java.io.IOException
2.发送响应消息头相关的方法

当Servlet向客户端发送响应消息时,由于HTTP协议的响应头字段有很多种,

为此在HttpServletResponse接口中,定义了一系列设置HTTP响应头字段的方法,如下图:

在这里插入图片描述

(1)`void setLocale(Locale loc):
  1. 对于HTTP协议,该方法就是设置Content-Language与Content-Type中的字符集编码部分
  2. 如果HTTP消息没有设置Content-Type头字段,则该方法设置的字符集编码不会出现在HTTP消息的响应头中
  3. 如果调用setCharacterEncoding()setContentType()方法指定了响应内容的字符集编码,该方法设置的字符集编码将失效
(2)`void setCharacterEncoding(String charset):
  1. 对于HTTP协议,该方法就是设置Content-Type中的字符集编码部分
  2. 如果没有设置Content-Type头字段,该方法设置的字符集编码也不会出现在HTTP消息的响应头中
  3. 该方法设置的字符集编码优先权最高,可覆盖setContentType与setLocale方法中的字符集设置

注意:6、7、8种方法都是用于设置字符编码,可有效解决乱码问题

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

在HTTP响应消息中,大量数据都是通过响应消息体传递的;

ServletResponse遵循以IO流传递数据的设计原理,在发送响应消息体时,定义了两个与输出相关的方法:

在这里插入图片描述

(1)`getOutputStream()

由于ServletOutputStream类型的对象可以直接输出字节数组中的二进制数据,

因此需要输出二进制格式的响应正文,就需要使用getOutputStream()方法

(2)`getWriter()

由于PrintWriter类型的对象可以直接输出字符文本内容,

因此需要输出字符文本的网页文档,就需要使用getWriter()方法

特别注意的是:虽然response对象的getOutputStream()getWriter()方法都可以发送响应消息体,但相互排除不可同时使用

4.HttpServletResponse应用案例
应用1:解决中文输出乱码的问题
(1)中文输出乱码的原因:

由于计算机数据都是以二进制形式存储的,在传输文本时将不可避免de 发生字符与字节的转换问题

补充:字符与字节的转换(字符与字节的转换是通过查码表完成的)

  • 编码:将字符转换为字节的过程
  • 解码:将字节转换为字符的过程

如果编码与解码使用的码表不一致,就会导致乱码问题的出现,因此需要需要考虑的方面如下:

考虑1:response对象的字符输出流在编码时,采用的字符码表(ISO-8859-1)

response对象默认采用的ISO-8859-1码表,并不兼容中文会将其编码为63(无法编码),

可以使用setCharacterEncoding()方法,对字符的编码方式进行设置。

response.setCharacterEncoding("utf-8");

考虑2:浏览器在进行解码时,采用的字符码表(GB2312)

浏览器默认采用的解码方式是GB2312,如果解码方式与编码方式不同将同样导致乱码问题。

可以使用setHeader()方法,对浏览器的解码方式进行设置。

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

注意:

根据前文一>2中的介绍,具有同样设置编码、解码效果的还有response对象提供的setContentType()方法:

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

该方法直接设置了编码、解码使用的码表,更加简洁的处理了中文乱码问题

(2)中文乱码问题的处理:
package c4p1;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class ChineseServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// response.setContentType("text/html;charset=utf-8");
		String data = "HttpServletResponse应用:使用Content-Type方法处理中文输出乱码问题";
		PrintWriter out = response.getWriter();
		out.println(data);
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

注意:xml配置文件已省略

在这里插入图片描述

在这里插入图片描述

应用2:实现网页定时刷新并跳转

在web开发中,有时需要实现定时跳转页面的需求,

在HTTP协议中,定义了一个Refresh头字段可以指定浏览器在指定时间内自动刷新/跳转到其他页面。

(1)实现网页定时跳转:
package c4p1;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class RefreshServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 两秒后刷新并跳转到csdn的首页
		response.setHeader("Refresh", "2;URL=https://www.csdn.net/");
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

在这里插入图片描述

(2)实现网页定时刷新:
package c4p1;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class RefreshServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setHeader("Refresh", "3"); //实现每3秒自动刷新
		response.getWriter().println(new java.util.Date()); //输出当前时间
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

在这里插入图片描述

应用3:实现请求重定向
(1)请求重定向问题:

某些情况下,针对客户端的请求一个Servlet类可能无法完成全部工作,这时可以使用请求重定向(多个Servlet)完成。

HttpServletResponse接口中定义了一个sendRedirect()方法,用于生成302响应码和Location响应头,

从而通知客户端重新访问Location响应头中的指定的URL,其完整语法如下:

public void sendRedirect(java.lang.String location) throws java.io.IOException

注意:参数location可以使用相对URL地址,Web服务器会自动将其翻译为绝对地址,再生成Location头字段

(2)请求重定向问题的实现:
package c4p2;

import java.io.IOException;

import org.apache.catalina.connector.Response;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset:utf-8");
		// 使用HttpServletRequest对象的getParameter()方法获取用户名和密码
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		// 实现请求重定向
		if (("itcast").equals(username) && ("123").equals(password)) {
			response.sendRedirect("/chapter04_reqandres/welcome.html");
		} else {
			response.sendRedirect("/chapter04_reqandres/login.html");
		}
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
<!DOCTYPE html>
<html>
	<head>
    	<meta http-equiv="Content-Tpye" content="text/html" charset="UTF-8">
    	<title>Insert title here</title>
	</head>
    
	<body>
    	<!-- 把表单内容提交到chapter04_reqandres工程下的LoginServlet -->
    	<form action="/chapter04_reqandres/LoginServlet" method="post">
            用户名:<input type="text" name="username" /><br />&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"><br />
        	<input type="submit" value="登录">
    	</form>
	</body>
</html>
<!DOCTYPE html>
<html>
	<head>
    	<meta http-equiv="Content-Tpye" content="text/html" charset="UTF-8">
    	<title>Insert title here</title>
	</head>
    
	<body>
		欢迎你!登录成功!
	</body>
</html>

在这里插入图片描述

注意:如果form表单post提交的路径更改为/chapter04_reqandres/WebContent/welcome.html将会找不到目标文件(有关文件路径问题)

二、HttpServletRequest对象

1.获取请求行消息的相关方法

当访问Servlet时,会在请求消息的请求行中包含请求方法、请求资源名、请求路径等信息。

为了获取这些信息,在HttpServletRequest接口中定义了一系列用于获取请求行的方法,具体使用如下所示:

package c4p3;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class RequestLineServlet extends HttpServlet{
	@Override
	protected 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("getScheme:" + request.getScheme() + "<br />");
		out.println("getRequestURI:" + request.getRequestURI() + "<br />");
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

在这里插入图片描述

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

当请求Servlet时,需要通过请求头向服务器传递附加信息,例如:客户端接收的数据类型、压缩方式、语言等。

HttpServletRequest接口中,定义了一系列用于获取HTTP请求头字段的方法:

在这里插入图片描述

package c4p3;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class RequestHeadersServlet extends HttpServlet{
	@Override
	protected 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 />");
		}
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

在这里插入图片描述

[增]利用Referer请求头防止盗链

在实际开发中经常使用Referer请求头字段,

利用Referer请求头字段可以实现,检查请求来源(只接受本站连接发送的下载请求,阻止其他站点链接的下载请求):

package c4p3;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class DownManagerServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html; charset = utf-8");
		PrintWriter out =response.getWriter();
		// 获取Referer头的值
		String referer = request.getHeader("referer");
		// 获取访问地址
		String sitePart = "http://" + request.getServerName();
		// 判断referer头是否为空,这个头的首地址是否以sitePart开始的
		if (referer != null && referer.startsWith(sitePart)) {
			// 处理正在下载的请求
			out.println("dealing download ...");
		} else {
			// 非法下载请求跳转到download.html页面
			RequestDispatcher rDispatcher = request.getRequestDispatcher("/download.html");
			rDispatcher.forward(request, response);
		}
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

在这里插入图片描述

结果分析1:在浏览器中输入地址直接访问DownManagerServlet(模拟其他站点链接的下载请求),由于第1次请求时请求消息中不含Referer请求头,所以DownManagerServlet将下载请求转发给了download.html页面

在这里插入图片描述

结果分析2:点击页面跳转后的download链接,这时由于请求消息中包含了Referer请求头,并且其值与DownManagerServlet位于同一个站点,因此DownManagerServlet接受了下载请求

3.HttpServletRequest应用案例
应用1:获取请求参数

在实际开中,经常需要获取用户提交的表单数据,如:用户名、密码、电子邮箱等。

ServeltRequest接口中,定义了一些获取请求参数的方法如下:

在这里插入图片描述

应用2:解决请求参数的中文乱码问题
(1)post提交方式的乱码问题处理:

form表单通过post方式提交会把参数首先提交到request对象的缓冲区中,缓冲区的默认编码是ISO-8859-1(不支持中文)

处理方式:将request缓冲区的编码方式设置为支持中文的码表格式:例如使用setCharacterEncoding()方法

request.setCharacterEncoding("utf-8");

注意:setCharacterEncoding()只对POST提交方式的乱码问题有效,而对GET方式无效

(2)get提交方式的乱码问题处理

对于get提交方式的乱码问题,使用String的构造方式来解决中文乱码问题:new String(bytes[], encodingMethod);

原理:先使用错误码表ISO-8859-1进行重新编码,然后使用码表utf-8进行解码。

String newvalue = new String(value.getBytes("ISO-8859-1"), "utf-8"); 

补充:对于GET请求参数的乱码问题,除了通过重新编码&解码,还可以通过配置Tomcat服务器来解决

应用3:通过Request对象传递数据

Request对象不仅可以获取一系列数据,还可以通过属性传递数据。

ServeltRequest接口中,定义了一些操作属性的方法,具体如下:

在这里插入图片描述

public void setAttribute(java.lang.String name, java.lang.Object o);
public java.lang.Object getAttribute(java.lang.String name);
public void removeAttribute(java.lang.String name);
public java.util.Enumeration getAttributeNames();

注意:只有属于同一个请求中的数据才能够通过ServletRequest对象进行数据传递

4.RequestDispatcher应用
(1)RequestDispatcher接口

除了使用sendRedirect()方法能够实现请求重定向外,还可以通过RequestDispatcher接口的实例对象来实现:

在ServletRequest接口中定义了一个获取RequestDispatcher对象的方法,如下:

RequestDispatcher getRequestDispatcher(String path)
/*
注释:
1.该方法返回封装了某个路径所指定资源的RequestDispatcher对象。
2.其中参数path指定必须以/开头,用于表示当前Web应用的根目录。
3.传递给getRequestDispatcher方法资源可以是WEB-INF目录中的文件,WEB-INF目录中的文件对RequestDispatcher对象是可见的。
*/

在这里插入图片描述

注意:根据RequestDispatcher对象提供方法功能,其中的forward方法可用于实现请求转发、include方法可用于实现请求包含

(2)`forward()请求转发

在Servlet中,如果当前Web资源不处理可以通过forward()方法将当前请求传递给其他的Web资源进行处理,称为请求转发

在这里插入图片描述

package c4p5;

import java.io.IOException;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class RequestForwardServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html; charset = utf-8");
		// 将数据存储到request对象中
		request.setAttribute("weather", "rainy");
		RequestDispatcher dispatcher = request.getRequestDispatcher("/ResultServlet");
		dispatcher.forward(request, response);
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package c4p5;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class ResultServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out =response.getWriter();
		// 从request对象中获取保存的数据
		String weather = (String) request.getAttribute("weather");
		if (weather != null) {
			out.println("今天的天气:" + weather + "<br />");
		}
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

页面分析:地址栏中显示的是RequestForwardServlet的请求路径,页面显示了ResultServlet的输出内容(实现了请求转发)

在这里插入图片描述

补充:请求转发与请求重定向的区别

  1. 请求重定向中有两次request请求、两次response响应,而请求转发有一次request请求、一次response响应
  2. 由于请求转发只有一次request请求,所以可以使用request属性进行数据共享
  3. 转发动作是在服务器内部实现的(转发路径不需要携带项目名称),重定向是在浏览器端执行的(路径需要项目名称)
(3)`include()请求包含

请求包含是使用include()方法将Servlet请求转发给其他Web资源进行处理,

与请求转发不同的是响应返回结果,既包含了当前Servlet的响应消息、也包含了其他Web资源作出的响应消息:

在这里插入图片描述

package c4p6;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class IncludingServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置字符编、解码使用码表必须在IncludingServlet类中设置
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		RequestDispatcher dispatcher = request.getRequestDispatcher("/IncludedServlet?parameter=abcdefg");
		out.println("before including" + "<br/>");
		dispatcher.include(request, response);
		out.println("after including" + "<br/>");
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
package c4p6;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class IncludedServlet extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		out.println("中国" + "<br/>");
		out.println("URL:" + request.getRequestURI() + "<br/>");
		out.println("QueryString:" + request.getQueryString() + "<br/>");
		out.println("Parameter:" + request.getParameter("parameter") + "<br/>");
	}
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

在这里插入图片描述

注意:设置字符编、解码使用码表必须在IncludingServlet类中设置,IncludedServlet中设置的码表并不起作用。

这是因为浏览器在请求IncludingServlet时,用于封装响应消息的HttpServletResponse对象已经创建(默认采用ISO-8859-1)。

更新日志:
2021/11/30,更新优化文章结构、部分图示重制美化

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值