4.2 请求与响应
4.2.1 响应 - HttpServletResponse
步骤:
- 目标:能响应中文到浏览器,能设计重定向
- 响应状态行
- 创建一个项目ch0406,创建一个CodeServlet.java文件,父类为HttpServlet。HttpServlet继承自GenericServlet,所以HttpServlet中重写了GenericServlet中的service方法:将req、res强制转换,再去调用HttpServlet类中的另一个service方法。
- 继承自HttpServlet,主要重写doGet和doPost方法。doPost方法中调用doGet方法。响应标头默认是200,通过setStatus方法可以修改响应标头(结果1)。也可以通过SendError方法修改
- 解决中文乱码问题
- 响应header
- 乱码产生:在doGet方法的getWriter方法中写入“中文”两个字,启动程序后页面出现乱码。
- 在setContentType中既要告诉服务器用utf-8编码,同时这个信息也会发送给浏览器,浏览器就知道了可以用utf-8方式解码。
- 重定向
- 创建一个TimeServlet.java文件,用来显示时间。
- 通过Date()获得当前时间,通过SimpleDateFormat把时间变成字符串形式,通过format()格式化日期。然后把字符串形式的dateStr添加到resp.getWriter方法下,输出在浏览器页面上
- 创建一个RedirectServlet.java,希望访问redirect时可以重定向过去:通过sendRedirect方法。sendRedirect的参数可以为time,也可以为req.getContextPath() + “/time”。
代码:
- 响应行状态:
@WebServlet("/code1")
public class CodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//(弃用方法) resp.setStatus(201);
resp.setCharacterEncoding("utf-8");
resp.sendError(404,"lost.....");
resp.getWriter().append("Served at: ").append(req.getContextPath());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
- 响应header:
@WebServlet("/chn")
public class ChineseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("school","cqrk");
// resp.setLocale(Locale.CHINA);
resp.setContentType("text/html;charset=utf-8");
//这个方法不保险 resp.setCharacterEncoding("utf-8");
resp.getWriter().append("中文002 Served at: ").append(req.getContextPath());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- 重定向
@WebServlet("/time")
public class TimeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyy/MM/dd HH:mm:ss");
String dateStr = sdf.format(now);
resp.getWriter().append("Served at: ").append(dateStr);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect(req.getContextPath() + "/time");
//resp.sendRedirect("time");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
实验结果:
- 响应行状态
结果1:响应标头用setStatus方法修改为201(该方法已被弃用)
结果2:响应标头用sendError方法修改为404(此时会出现中文乱码)
结果3:在sendError方法前加上setCharacterEncoding方法,参数为utf-8
2. 响应header
结果1: 产生乱码
结果2: 使用setContentType解决乱码
3. 重定向
结果1: time页面显示时间
结果2: redirect页面重定向
4.2.2 响应图片
步骤:
- 用Java GUI生成验证码图片,使用Java imageio把图片写到输出流。
- 在webapp文件夹下创建一个login.html,里面写了一个image标签,当点击这张图片时,执行script里面的代码
代码:
@WebServlet("/check")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. content-type 返回图片类型
/**ServletContext功能之一:获取MIME类型:
* MIME类型:在互联网通信过程中定义的一种文件数据类型
* 格式: 大类型/小类型 text/html image/jpeg
* 获取:String getMimeType(String file)
* */
String mimeType = getServletContext().getMimeType(".jpg");
System.out.println("mimetype" + mimeType);
resp.setContentType(mimeType);
//不缓存
//resp.setHeader("Cache-Control","no-store");
//2. 画图大小
int w = 100;
int h = 50;
//3. 生成BufferedImage对象
BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics graphics = bufferedImage.getGraphics();
//4. 填充矩形
graphics.setColor(Color.LIGHT_GRAY);//颜色
graphics.fillRect(0, 0, w, h);//矩形从左上角(0,0)到(x,y)
//5. 画外框
graphics.setColor(new Color(34, 34, 250));//先设置一个颜色
graphics.drawRect(0, 0, w-1, h-1);//画一个矩形框
//6. 在这张图片上写4个字母
graphics.setFont(new Font("",Font.BOLD,20));
Random random = new Random();
for (int i = 0; i < 4; i++){ //随机生成4个字符,画在图片里
char x = (char) ('A' + random.nextInt(25));
graphics.drawString(""+x, 5+i*20, 30);
}
//7. 画随机线条
for(int i = 0; i < 10; i++){
int x1 = random.nextInt(100);
int x2 = random.nextInt(100);
int y1 = random.nextInt(50);
int y2 = random.nextInt(50);
graphics.drawLine(x1, y1, x2, y2);
}
//8. 把图片写出到输出流
ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--当点击这张图片时,就会执行下面的script代码-->
img: <img id="code" alt="" src="check" onclick="freash()">
<script>
function freash(){
let check = document.getElementById("code");
check.src="check?"+new Date().getTime();
console.log("freash2....")//控制台上输出freash....
}
</script>
</body>
</html>
实验结果:
4.2.3 请求行、请求头获取
步骤:
- 创建一个RequestLineServlet.java文件,继承自HttpServlet
- 在doGet方法中,设置ContentType为中文,通过resp.getWriter方法输出请求行的相关信息
- 创建一个RequestHeaderServlet.java文件,继承自HttpServlet
- 请求消息过来后,除了请求行,还有很多的请求头,这些请求头怎么去取数据?
- 设置响应的ContentType,拿到getWriter,通过调用req的getHeaderNames方法拿到所有头字段。再写个循环去遍历取出headerName,通过req.getHeader把headerName传进去。就能取出header的值
代码:
@WebServlet("/reqTest")
public class RequestLineServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
//获取请求行的相关信息
out.println("getMethod : " + req.getMethod() + "<br>");
out.println("getRequestURI : " + req.getRequestURI() + "<br>");
out.println("getQueryString : " + req.getQueryString() + "<br>");
out.println("getProtocol : " + req.getProtocol() + "<br>");
out.println("getContextPath : " + req.getContextPath() + "<br>");
out.println("getPathInfo : " + req.getPathInfo() + "<br>");
out.println("getPathTranslated : " + req.getPathTranslated() + "<br>");
out.println("getServletPath : " + req.getServletPath() + "<br>");
out.println("getRemoteAddr : " + req.getRemoteAddr() + "<br>");
out.println("getRemoteHost : " + req.getRemoteHost() + "<br>");
out.println("getRemotePort : " + req.getRemotePort() + "<br>");
out.println("getLocalAddr : " + req.getLocalAddr() + "<br>");
out.println("getLocalName : " + req.getLocalName() + "<br>");
out.println("getLocalPort : " + req.getLocalPort() + "<br>");
out.println("getServerName : " + req.getServerName() + "<br>");
out.println("getServerPort : " + req.getServerPort() + "<br>");
out.println("getScheme : " + req.getScheme() + "<br>");
out.println("getRequestURL : " + req.getRequestURL() + "<br>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/reqHeader")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
//获取请求消息中所有头字段
Enumeration<String> headerNames = req.getHeaderNames();
//使用循环遍历所有请求头,并通过getWriter()方法获取一个指定名称的头字段
while (headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
out.println(headerName + ":" + req.getHeader(headerName) + "<br>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
实验结果:
4.2.4 请求参数的获取
步骤:
- 表单提交数据
- 表单form.html:form标签的action特性:当用户提交时,把数据提交给指定的url。method用来指定Http请求的方式:get。
- 下面是用户名、密码标签,爱好复选框,提交按钮submit,当用户点击提交按钮,表单就会把数据提交给URL,这个URL对应的RequestParamsServlet下面的@WebServlet。
- 服务端程序获取参数
- 当用户点击提交按钮后,数据就会提交给action。如果method是get请求,tomcat就会调用doGet方法;如果method为post,tomcat就会去调用doPost方法。doPost方法中会转去调用doGet方法
- 通过req.getParameter方法获得用户名、密码对应的值,在控制台输出。复选框用户有可能会选多个,所以通过req.getParameterValues()得到二维数组hobbies,然后把爱好输出在控制台。
- 解决参数乱码问题
- 如果提交的表单中用户名是中文,控制端输出为乱码
- 加一句req.setCharacterEncoding(“utf-8”);//这里填utf-8是因为在form.html中的content中为utf-8,即浏览器发送的编码是content中的utf-8,解码的时候指定也要是utf-8
- 问题:请求参数的获取:在实际开发中,经常需要获取用户提交的表单数据。例如,用户名、密码、电子邮件,那么处理程序怎么得到用户填写的内容呢?这些信息可能出现在url中(get请求,把数据通过get方法传过来),在请求体当中(post请求)
- 解决办法:在HttpServlet接口中,定义了一系列获取请求参数的方法。
- String getParameter(String name)获取某个指定名称的参数值。
- String[] getParameterValues(String name)如果要获得HTTP请求消息中的同一个参数名对应的所有参数值
- Enumeration getParameterNames()该方法返回一个包含请求消息中所有参数名的Enumeration对象,在此基础上,可以对请求消息中的所有参数进行遍历处理。
- Map getParameterMap()用于将请求消息中的所有参数名和值装入进一个Map对象中返回
代码:
@WebServlet("/RequestParamServlet")
public class RequestParamsServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String name = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("用户名:" + name);
System.out.println("密 码:" + password);
//获取参数名为hobby的值
String[] hobbies = req.getParameterValues("hobby");
System.out.println("爱好:");
if(hobbies != null){
for (String hobby1 : hobbies) {
System.out.println(hobby1 + ",");
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
实验结果:
- 表单提交数据(get和post两种方法)
改成post方法:
- 通过HttpServletRequest获取请求参数
4.2.5 请求转发forward
步骤:
- 请求转发:
当浏览器发送请求,访问Servlet1,在Web服务器端的Servlet容器中servlet1通过forward()方法把这个请求交给其他Web资源进行处理,其他Web资源再把响应结果给浏览器。 - 请求包含:
当浏览器发送请求,访问Servlet1,在Web服务器端的Servlet容器中通过RequestDispatcer调用include()方法把Servlet1和其他web资源一起响应浏览器,把结果给浏览器。
- forward基本使用
- 通过req.getRequestDispatcher,参数填URL:/SecondServlet,得到一个RequestDispatcher类型的变量,定义为dispatcher
- 在FirstServlet通过req.setAttribute方法把数据放到request域对象里面,在SecondServlet通过req.getAttribute方法把数据取出来、展示出来。(一个Servlet负责处理数据,另一个Servlet负责把数据展示出来,就可以把代码分开)
- 调用resp的getWriter方法将数据写到缓存器,tomcat没有发送,调用RequestdDispatcher的forward()方法
代码:
get方法请求转发:
@WebServlet("/FirstServlet")
public class FirstServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/SecondServlet");
req.setAttribute("msg","sqrk lalal");
resp.getWriter().append("first 01 Served at: ").append(req.getContextPath());
dispatcher.forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/SecondServlet")
public class SecondServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object msg = req.getAttribute("msg");
resp.getWriter().append("second Served at: " + msg).append(req.getContextPath());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
post方法请求转发:
@WebServlet("/FirstServlet2")
public class FirstServlet2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/SecondServlet2");
req.setAttribute("msg","cqrk1234");
resp.getWriter().append("first 0001 Served at: ").append(req.getContextPath());
dispatcher.include(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/SecondServlet2")
public class SecondServlet2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("school","cqrk");
Object msg = req.getAttribute("msg");
resp.getWriter().append("second 0002 Served at: " + msg).append(req.getContextPath());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
实验结果:
4.2.6 请求包含
步骤:
forward方法:会把请求转发过去,当前的Servlet不会对请求进行响应。
include方法:两个servlet产生的内容都会出现在浏览器端
- 在FirstServlet中将forward方法改为include方法
- 在SecondServlet中加一句setHeader方法,添加一个school\cqrk
代码:
@WebServlet("/FirstServlet2")
public class FirstServlet2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher dispatcher = req.getRequestDispatcher("/SecondServlet2");
req.setAttribute("msg","cqrk1234");
resp.getWriter().append("first 0001 Served at: ").append(req.getContextPath());
dispatcher.include(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/SecondServlet2")
public class SecondServlet2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("school","cqrk");
Object msg = req.getAttribute("msg");
resp.getWriter().append("second 0002 Served at: " + msg).append(req.getContextPath());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
实验结果:
FirstServlet和SecondServlet的内容都显示出来了
响应标头中并没有setHeader中的内容
4.2.7 作业:简单的验证登录
步骤:
目的:
- 静态页面:login.html登录表单(至少包含用户名、密码)
home.html模拟登录后可见的数据管理页面 - LoginServlet对login.html提交的用户名-密码验证,通过验证(abc, 123)则显示home.html,如果不成功,则让用户继续登录
- 扩展:登录后显示登录用户名
步骤: - login.html表单中action代表数据要提交到哪里去,method使用post请求方式,两个文本框账号、密码,一个提交按钮
- home.jsp表单中,模拟用户登录成功后的页面
- 访问home静态页面时,因为home在WEB-INF下,不能被浏览器直接访问。Home页面需要用户登录成功后,才能访问到
- loginServlet中通过req.getParameter得到用户账号和密码,判断是否跟abc、123相同
- 如果验证的话,通过req.setAttribute()将用户输入的username保存到域对象里,home.jsp取到传过来的域对象,把用户名显示到页面上。通过req.getRequestDispatcher调用forward方法跳转去home页面
- 如果验证不成功,就重定向到login
代码:
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
if("abc".equals(username) && "123".equals(pwd)){
req.setAttribute("name",username);
req.getRequestDispatcher("/WEB-INF/home.jsp").forward(req,resp);
}else {
resp.sendRedirect("/ch0407_war_exploded/login.html");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--post和get都可以-->
<form action="login" method="post">
<input type="text" name="username" placeholder="账号" />
<input type="text" name="pwd" placeholder="密码"/>
<input type="submit" value="登陆">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>管理首页</h1>
<h4>登陆用户:${name}</h4>
添加成绩<br/>
修改成绩<br/>
....<br/>
</body>
</html>
实验结果: