一、request
咱们在上节课已经学过 对于servlet的基本使用 我们可以看到无论是在service方法中还是在doget和dopost方法中 都有两个形参 request response 那我们这节课就来学习请求和响应。
1.1 请求响应流程介绍
快速入门
http://localhost:8080/servlet-demo/demo1?user=zhangsan
@WebServlet("/demo1")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.使用request获取请求数据
String user = request.getParameter("user");//url?user=zhangsan
//2.使用response将数据响应到页面中
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("<h1>"+user+",欢迎你</h1>");
}
1.2 Request继承体系
在学习这节内容之前,我们先思考一个问题,前面在介绍Request和Reponse对象的时候,比较细心的同学可能已经发现:
- 当我们的Servlet类实现的是Servlet接口的时候,service方法中的参数是ServletRequest和ServletResponse
- 当我们的Servlet类继承的是HttpServlet类的时候,doGet和doPost方法中的参数就变成HttpServletRequest和HttpServletReponse
那么,
-
ServletRequest和HttpServletRequest的关系是什么?
-
request对象是有谁来创建的?
-
request提供了哪些API,这些API从哪里查?
-
首先,我们先来看下Request的继承体系:
通过查看ServletRequest和HttpServletRequest是继承关系,并且两个都是接口,接口是无法创建对象,那么形参是由谁创建的呢?
这个时候,我们就需要用到Request继承体系中的RequestFacade
:
- 该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
- Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
- RequestFacade中实现了其接口就需要重写接口中的方法
验证RequestFacade:
@WebServlet("/demo2")
public class ServletDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request);//org.apache.catalina.connector.RequestFacade@6158eeb4
}
1.3 获取获取请求数据(行,头)
package com.itgaohe.web;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/*
* 1. 继承HttpServlet 重写doGet doPost this.doPost()
* 2. 写映射
* 3. 写功能
*
* */
@WebServlet("/requestdemo1")
public class RequestDemo1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//一切请求由post解决
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("你来拉");
// 获取请求行里的信息 数据是由发起者(浏览器)
// 1.获取请求方式
String method = req.getMethod();
System.out.println("获取请求方式:"+method);
// 2.获取虚拟目录(项目访问路径): /webservlet
String contextPath = req.getContextPath();
System.out.println("获取虚拟目录:"+contextPath);
// 3.获取URL(统一资源定位符):请求的网址,不是发起的页面。http://localhost:81/webservlet/requestdemo1
StringBuffer requestURL = req.getRequestURL();
System.out.println("获取URL(统一资源定位符):"+requestURL);
// 4. 获取URI(统一资源标识符)(更长用):/webservlet/requestdemo1
String requestURI = req.getRequestURI();
System.out.println("获取URI(统一资源标识符):"+requestURI);
// 获取请求头
String header = req.getHeader("Accept-Encoding");
System.out.println("请求头Accept-Encoding="+header);
//获取请求头: user-agent: 浏览器的版本信息
String agent = req.getHeader("user-agent");
System.out.println(agent);
}
}
1.4 获取获取请求数据(请求体)
在获取请求体数据的数据 我们先创建一个页面 method请求方式为post
具体实现的步骤如下:
1.准备一个页面,在页面中添加form表单,用来发送post请求
2.在Servlet的doPost方法中获取请求体数据
3.在doPost方法中使用request的getReader()或者getInputStream()来获取
4.访问页面测试
<!--
action:form表单提交的请求地址
method:请求方式,指定为post
-->
调用getReader()或者getInputStream()方法,因为目前前端传递的是纯文本数据,所以我们采用getReader()方法来获取
注意事项:
1.只能接收post请求的数据 doPost处理
2.流获取到的数据参数数据
3. 一个请求只能开启一个网络流
4.一旦使用了参数去接收数据,就不能使用流接收 getParamter方法冲突
请求体读取
/**
* request 获取请求数据
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//一切请求由post解决
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取post 请求体:请求参数
// 注意事项:
// 1.只能接收post请求的数据 doPost处理
// 2.流获取到的数据参数数据
// 3. 一个请求只能开启一个网络流
// 4.一旦使用了参数去接收数据,就不能使用流接收 getParamter方法冲突
// 用流的方式获取请求数据(了解):文件上传
// 字节流(上传)request.getInputStream()
// 1.1 字节流
// InputStream is = req.getInputStream();
// byte[] bytes = new byte[1024];
// is.read(bytes);
// is.close();
// System.out.println(new String(bytes));
// 1.2字节流(解决空格问题)
// InputStream is = request.getInputStream();
// 字符转换流
// InputStreamReader isr = new InputStreamReader(is, "utf-8");
// char[] chars = new char[1024];
// int len;
// while ((len = isr.read(chars))!=-1){
// line = new String(chars, 0, len);
// }
// System.out.println(line);
// 2.字符流
// 1. 获取字符输入流
// BufferedReader reader = req.getReader();
// String line = reader.readLine();
// System.out.println("readerline="+line);
}
}
1.5 获取统一获取请求数据(请求参数)
package com.itgaohe.web;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/*
* 1. 继承HttpServlet 重写doGet doPost this.doPost()
* 2. 写映射
* 3. 写功能
*
* */
@WebServlet("/requestdemo1")
public class RequestDemo1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 5.获取请求参数(GET方式)(做拦截会用):
String queryString = req.getQueryString();
System.out.println("获取请求参数(GET方式): "+queryString);
// 获取请求体 - 请求的数据
// 1.获取单个参数(重点)
String username = req.getParameter("username");
System.out.println("username="+username);
String password = req.getParameter("password");
System.out.println("password="+password);
String hoppy = req.getParameter("hoppy");//不能拿多个数据,只能拿一个
System.out.println("hoppy1="+hoppy);
// 3.获取一个键对应的多个数据
String[] hoppies = req.getParameterValues("hoppy");
System.out.println("hoppy2="+Arrays.toString(hoppies));
// 2.获取数据键值对结合
Map<String, String[]> map = req.getParameterMap();
Set<String> keySet = map.keySet();
for (String key : keySet) {
// 通过键获取值
String[] values = map.get(key);
for (String value : values) {
// 打印一下具体的键和值
System.out.println("key="+key +"------" +"value="+value);
}
}
}
}
1.6 IDEA快速创建模板
略。
1.7 请求参数中文乱码问题
问题展示:
(1)将req.html页面的请求方式修改为get
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-demo/req2" method="get">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="checkbox" name="hobby" value="1"> 游泳
<input type="checkbox" name="hobby" value="2"> 爬山 <br>
<input type="submit">
</form>
</body>
</html>
(2)在Servlet方法中获取参数,并打印
/**
* 中文乱码问题解决方案
*/
@WebServlet("/req2")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取username
String username = request.getParameter("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)启动服务器,页面上输入中文参数
(4)查看控制台打印内容(乱码)
(5)把req.html页面的请求方式改成post,再次发送请求和中文参数
(6)查看控制台打印内容,依然为乱码
通过上面的案例,会发现,不管是GET还是POST请求,在发送的请求参数中如果有中文,在后台接收的时候,都会出现中文乱码的问题。具体该如何解决呢?
1.7.1 Post方式请求参数中文乱码处理
分析出现中文乱码的原因:
POST的请求参数是通过request的getReader()来获取流中的数据
TOMCAT在获取流的时候采用的编码是ISO-8859-1
ISO-8859-1编码是不支持中文的,所以会出现乱码
1.7.2 POST请求解决方案
页面设置的编码格式为UTF-8
把TOMCAT在获取流数据之前的编码设置为UTF-8
通过request.setCharacterEncoding(“UTF-8”)设置编码,UTF-8也可以写成小写
1. 解决乱码: POST getReader()
//设置字符输入流的编码,设置的字符集要和页面保持一致
request.setCharacterEncoding("UTF-8");
重新发送POST请求,就会在控制台看到正常展示的中文结果。
至此POST请求中文乱码的问题就已经解决,但是这种方案不适用于GET请求,这个原因是什么呢,咱们下面再分析。
1.7.3 GET请求解决方案
刚才提到一个问题是POST请求的中文乱码解决方案为什么不适用GET请求?
- GET请求获取请求参数的方式是
request.getQueryString()
- POST请求获取请求参数的方式是
request.getReader()
- request.setCharacterEncoding(“utf-8”)是设置request处理流的编码
- getQueryString方法并没有通过流的方式获取数据
所以GET请求不能用设置编码的方式来解决中文乱码问题,那问题又来了,如何解决GET请求的中文乱码呢?
- 首先我们需要先分析下GET请求出现乱码的原因:
(1)浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)
(2)浏览器在发送HTTP的过程中会对中文数据进行URL编码
(3)在进行URL编码的时候会采用页面<meta>
标签指定的UTF-8的方式进行编码,张三
编码后的结果为%E5%BC%A0%E4%B8%89
(4)后台服务器(Tomcat)接收到%E5%BC%A0%E4%B8%89
后会默认按照ISO-8859-1
进行URL解码
(5)由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码。
思考: 如果把req.html
页面的<meta>
标签的charset属性改成ISO-8859-1
,后台不做操作,能解决中文乱码问题么?
答案是否定的,因为ISO-8859-1
本身是不支持中文展示的,所以改了标签的charset属性后,会导致页面上的中文内容都无法正常展示。
分析完上面的问题后,我们会发现,其中有两个我们不熟悉的内容就是URL编码和URL解码,什么是URL编码,什么又是URL解码呢?
URL编码
这块知识我们只需要了解下即可,具体编码过程分两步,分别是:
(1)将字符串按照编码方式转为二进制
(2)每个字节转为2个16进制数并在前边加上%
张三
按照UTF-8的方式转换成二进制的结果为:
1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001
这个结果是如何计算的?
使用http://www.mytju.com/classcode/tools/encode_utf8.asp
,输入张三
就可以获取张和三分别对应的10进制,然后在使用计算器,选择程序员模式,计算出对应的二进制数据结果:
在计算的十六进制结果中,每两位前面加一个%,就可以获取到%E5%BC%A0%E4%B8%89
。
当然你从上面所提供的网站中就已经能看到编码16进制的结果了:
但是对于上面的计算过程,如果没有工具,纯手工计算的话,相对来说还是比较复杂的,我们也不需要进行手动计算,在Java中已经为我们提供了编码和解码的API工具类可以让我们更快速的进行编码和解码:
编码:
java.net.URLEncoder.encode("需要被编码的内容","字符集(UTF-8)")
解码:
java.net.URLDecoder.decode("需要被解码的内容","字符集(UTF-8)")
接下来咱们对张三
来进行编码和解码
public class URLDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String username = "张三";
//1. URL编码
String encode = URLEncoder.encode(username, "utf-8");
System.out.println(encode); //打印:%E5%BC%A0%E4%B8%89
//2. URL解码
//String decode = URLDecoder.decode(encode, "utf-8");//打印:张三
String decode = URLDecoder.decode(encode, "ISO-8859-1");//打印:`å¼ ä¸ `
System.out.println(decode);
}
}
到这,我们就可以分析出GET请求中文参数出现乱码的原因了,
- 浏览器把中文参数按照
UTF-8
进行URL编码 - Tomcat对获取到的内容进行了
ISO-8859-1
的URL解码 - 在控制台就会出现类上
å¼ ä¸‰
的乱码,最后一位是个空格
- 清楚了出现乱码的原因,接下来我们就需要想办法进行解决
从上图可以看住,
-
在进行编码和解码的时候,不管使用的是哪个字符集,他们对应的
%E5%BC%A0%E4%B8%89
是一致的 -
那他们对应的二进制值也是一样的,为:
-
1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001
-
-
为所以我们可以考虑把
å¼ ä¸‰
转换成字节,在把字节转换成张三
,在转换的过程中是它们的编码一致,就可以解决中文乱码问题。
具体的实现步骤为:
1.按照ISO-8859-1编码获取乱码
å¼ ä¸‰
对应的字节数组2.按照UTF-8编码获取字节数组对应的字符串
实现代码如下:
public class URLDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String username = "张三";
//1. URL编码
String encode = URLEncoder.encode(username, "utf-8");
System.out.println(encode);
//2. URL解码
String decode = URLDecoder.decode(encode, "ISO-8859-1");
System.out.println(decode); //此处打印的是对应的乱码数据
//3. 转换为字节数据,编码
byte[] bytes = decode.getBytes("ISO-8859-1");
for (byte b : bytes) {
System.out.print(b + " ");
}
//此处打印的是:-27 -68 -96 -28 -72 -119
//4. 将字节数组转为字符串,解码
String s = new String(bytes, "utf-8");
System.out.println(s); //此处打印的是张三
}
}
说明:在第18行中打印的数据是-27 -68 -96 -28 -72 -119
和张三
转换成的二进制数据1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001
为什么不一样呢?
其实打印出来的是十进制数据,我们只需要使用计算机换算下就能得到他们的对应关系,如下图:
至此对于GET请求中文乱码的解决方案,我们就已经分析完了,最后在代码中去实现下:
1.7.3 最终代码
/**
* 中文乱码问题解决方案
*/
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 解决乱码:POST,getReader()
//request.setCharacterEncoding("UTF-8");//设置字符输入流的编码
//2. 获取username
String username = request.getParameter("username");
System.out.println("解决乱码前:"+username);
// 解决办法1.
// 按照"ISO-8859-1" 编码
//String encode = URLEncoder.encode(username, "ISO-8859-1");
//System.out.println(encode);
// 按照"utf-8" 解码
//String decode = URLDecoder.decode(encode, "utf-8");
//System.out.println(decode);
//解决办法2
//3. GET,获取参数的方式:getQueryString
// 乱码原因:tomcat进行URL解码,默认的字符集ISO-8859-1
/* //3.1 先对乱码数据进行编码:转为字节数组
byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
//3.2 字节数组解码
username = new String(bytes, StandardCharsets.UTF_8);*/
username = new String(username.getBytes(StandardCharsets.ISO_8859_1),
StandardCharsets.UTF_8);
System.out.println("解决乱码后:"+username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
注意
- 把
request.setCharacterEncoding("UTF-8")
代码注释掉后,会发现GET请求参数乱码解决方案同时也可也把POST请求参数乱码的问题也解决了 - 只不过对于POST请求参数一般都会比较多,采用这种方式解决乱码起来比较麻烦,所以对于POST请求还是建议使用设置编码的方式进行。
另外需要说明一点的是Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8
1.8 小结
- 中文乱码解决方案
-
POST请求和GET请求的参数中如果有中文,后台接收数据就会出现中文乱码问题
GET请求在Tomcat8.0以后的版本就不会出现了
-
POST请求解决方案是:设置输入流的编码
request.setCharacterEncoding("UTF-8"); 注意:设置的字符集要和页面保持一致
-
通用方式(GET/POST):需要先解码,再编码
new String(username.getBytes("ISO-8859-1"),"UTF-8");
- URL编码实现方式:
-
编码:
URLEncoder.encode(str,"UTF-8");
-
解码:
URLDecoder.decode(s,"ISO-8859-1");
二、response
前面讲解完Request对象,接下来我们回到刚开始的那张图:
- Request:使用request对象来获取请求数据
- Response:使用response对象来设置响应数据
Reponse的继承体系和Request的继承体系也非常相似:
1.响应头行体+响应字符数据
package com.itgaohe.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ResponseDemo1Servlet")
public class ResponseDemo1Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应行-- 状态码
response.setStatus(202);
// 设置响应头
// 设置文本响应格式 text/html
// response.setHeader("content-type","text/html;charset=utf-8");
// 处理相应数据格式的
response.setContentType("text/html;charset=utf-8");
// 响应数据-- 字符流响应
response.getWriter().write("<h1>哈哈哈</h1>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doPost(request, response);
}
}
2.Response 响应字节数据
eg:字节流往前台刷图片
package com.itgaohe.web;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@WebServlet("/imgs")
public class ResponseDemo2Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 字节流
// OutputStream os = response.getOutputStream();
// 案例刷图片
// 图片路径-读取图片
InputStream is = new FileInputStream("E:\\images\\a.png");
// 获取网络字节流流 - 写
OutputStream os = response.getOutputStream();
// 一组一组的写
byte[] bytes = new byte[1024];
int len = -1;
while ((len = is.read(bytes))!= -1){
os.write(bytes,0,len);
}
// IOUtils.copy(is,os);
// 关闭本地流
is.close();
// 网络流 不需要手动关闭,会在请求结束后自动关闭
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doPost(request, response);
}
}
导入坐标
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
// IOUtils.copy(is,os);
3.download下载
案例:尝试把本地图片刷新到浏览器
步骤:
1.创建本地流读
2.设置响应头
response.addHeader("Content-Disposition","attachment;filename="+UUID.randomUUID()+".png");
3.创建网络输出流
4.文件读写
5.关闭流
package com.itgaohe.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
@WebServlet("/download")
public class ResponseDemo3Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 字节流
// OutputStream os = response.getOutputStream();
// 案例提供下载事件 filename就是下载的文件名字
response.addHeader("Content-Disposition","attachment;filename="+ UUID.randomUUID()+".png");
// 图片路径-读取图片
InputStream is = new FileInputStream("E:\\images\\a.png");
// 获取网络字节流流 - 写
OutputStream os = response.getOutputStream();
// 一组一组的写
byte[] bytes = new byte[1024];
int len = -1;
while ((len = is.read(bytes))!= -1){
os.write(bytes,0,len);
}
// 关闭本地流
is.close();
// 网络流 不需要手动关闭,会在请求结束后自动关闭
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doPost(request, response);
}
}
三、请求转发重定向
3.1请求转发
- 请求转发(forward):一种在服务器内部的资源跳转方式。
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A处理完请求后将请求发给资源B
(3)资源B处理完后将结果响应给浏览器
(4)请求从资源A到资源B的过程就叫请求转发
@WebServlet("/AServlet")
public class AServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我没有300块");
// 请求转发
// 特点:
// 1.浏览器地址栏路径不发生变化
// 2.不可以请求其他服务器 只能转发到当前服务器的内部资源 http://www.baidu.com
// 3. 性能比较快 一次请求,可以在转发的资源间使用request共享数据
request.setAttribute("username","张三");
request.getRequestDispatcher("BServlet").forward(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doGet(request, response);
}
}
@WebServlet("/BServlet")
public class BServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我有300块");
Object username = request.getAttribute("username");
System.out.println(username);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("我可以借给你300块");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doGet(request, response);
}
}
3.2 重定向
- Response重定向(redirect):一种资源跳转方式。
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
@WebServlet("/CServlet")
public class CServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我没有300块,D有300块,你自己去找");
// response.setStatus(302);
// response.setHeader("location","DDServlet");
// System.out.println("我来!");
// 重定向
// 特点:1. 浏览器地址栏路径发生变化
// 2. 可以重定向到任意位置的资源(服务器内部、外部均可)
// 3. 性能较差 两次请求,不能在多个资源使用request共享数据
// 4.绝对路径/DServlet 端口号后面的项目名会省略 http://localhost:81/DServlet
response.sendRedirect("DDServlet");
// response.sendRedirect("/webservlet/DServlet");
// response.sendRedirect(request.getContextPath()+"/DServlet");
// response.sendRedirect("https://www.baidu.com");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由get解决
this.doGet(request, response);
}
}
Dservlet
@WebServlet("/DServlet")
public class DServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我有300块");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("我可以借给你300块");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doGet(request, response);
}
}
3.3 重定向和请求转发对比
3.4 路径问题
问题导入:项目名称路径!什么时候加什么时候不加?
请求转发不需要加 重定向在使用绝对路径的时候需要加虚拟路径 在使用相对路径的时候则不需要加 此时不能加/
- 问题1:转发的时候路径上没有加
/request-demo
而重定向加了,那么到底什么时候需要加,什么时候不需要加呢?
其实判断的依据很简单,只需要记住下面的规则即可:
- 浏览器使用:需要加虚拟目录(项目访问路径)
- 服务端使用:不需要加虚拟目录
对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。
掌握了这个规则,接下来就通过一些练习来强化下知识的学习:
<a href='路劲'>
<form action='路径'>
- req.getRequestDispatcher(“路径”)
- resp.sendRedirect(“路径”)
答案:
1.超链接,从浏览器发送,需要加
2.表单,从浏览器发送,需要加
3.转发,是从服务器内部跳转,不需要加
4.重定向,是由浏览器进行跳转,需要加。
- 问题2:在重定向的代码中,
/request-demo
是固定编码的,如果后期通过Tomcat插件配置了项目的访问路径,那么所有需要重定向的地方都需要重新修改,该如何优化?
答案也比较简单,我们可以在代码中动态去获取项目访问的虚拟目录,具体如何获取,我们可以借助前面咱们所学习的request对象中的getContextPath()方法,修改后的代码如下:
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
//简化方式完成重定向
//动态获取虚拟目录
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/resp2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
重新启动访问测试,功能依然能够实现,此时就可以动态获取项目访问的虚拟路径,从而降低代码的耦合度。
四、请求和响应API小结
请求API
String getMethod() | 获取请求方式: GET |
---|---|
String getContextPath() | 获取虚拟目录(项目访问路径): /request-demo |
StringBuffer getRequestURL() | 获取URL(统一资源定位符): http://localhost:8080/request-demo/req1 |
String getRequestURI() | 获取URI(统一资源标识符): /request-demo/req1 |
String getQueryString() | 获取请求参数(GET方式): username=zhangsan&password=123 |
String getHeader(String name) | 获取请求头信息 |
ServletInputStream getInputStream() | 获取字节流(一般用于上传功能)(只能接收处理post请求) |
BufferedReader getReader() | 获取字符流(只能接收处理post请求) |
req.getRequestDispatcher(“资源B路径”).forward(req,resp); | 请求转发 |
响应API
void setStatus(int sc); | 设置状态码 |
---|---|
void setHeader(String name,String value); | 设置响应头键值对: |
PrintWriter getWriter(); | 获取字节输出流(一般输出文本) |
ServletOutputStream getOutputStream(); | 获取字符输出流(一般输出文件) |
sendRedirect(“路径”); | 重定向 |
setContentType(“text/html;charset=utf-8”); | 设置响应数据格式:html并且utf-8编码 |
addHeader(“Content-Disposition”,“attachment;filename=a.png”); | 文件下载格式必须设置响应头信息 |
五、登录注册
需求分析
- 用户在登录页面输入用户名和密码,提交请求给LoginServlet
- 在LoginServlet中接收请求和数据[用户名和密码]
- 在LoginServlt中通过Mybatis实现调用UserMapper来根据用户名和密码查询数据库表
- 将查询的结果封装到User对象中进行返回
- 在LoginServlet中判断返回的User对象是否为null
- 如果为nul,说明根据用户名和密码没有查询到用户,则登录失败,返回"登录失败"数据给前端
- 如果不为null,则说明用户存在并且密码正确,则登录成功,返回"登录成功"数据给前端
环境准备
准备须知:
1.创建模块weblogin
2.补全目录、pom修改打包方式war、pom配置tomcat依赖、servlet依赖
3.把前端工程师开发的页面导入
4.搭建数据环境建库导入数据
5.搭建mybatis环境---test测试一下环境是否ok
5.1 mybatis依赖、数据库依赖、(日志依赖可以有)、junit
5.2 mybatis核心配置文件
5.3 mybatis工厂工具类
5.4 mybatis接口
5.5 junit测试
6.写功能
6.1 写数据库查询sql mapper层
6.2 写servlet接收用户的登录信息类
6.3 页面表单请求路径配置
7.测试功能
环境准备============
-
项目目录
-
pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itgaohe</groupId> <artifactId>weblogin</artifactId> <version>1.0-SNAPSHOT</version> <!--1--> <packaging>war</packaging> <!--3--> <dependencies> <!--servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> </dependencies> <!--2--> <build> <plugins> <!--Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <!--端口号--> <port>81</port> <!--项目访问路径--> <path>/weblogin</path> </configuration> </plugin> </plugins> </build> </project>
-
页面略参考资料
-
数据库sql
-- 创建用户表 CREATE TABLE tb_user( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) UNIQUE, PASSWORD VARCHAR(32) ); -- 添加数据 INSERT INTO tb_user(username,PASSWORD) VALUES('zhangsan','123'),('lisi','234'); SELECT * FROM tb_user;
-
pojo略
-
mybatis核心
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--起别名--> <typeAliases> <package name="com.itgaohe.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///login?useSSL=false&useServerPrepStmts=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!--扫描mapper--> <package name="com.itgaohe.mapper"/> </mappers> </configuration>
-
mybatis工具类
package com.itgaohe.util; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MybatisUtil { // 获取sqlsession public static SqlSession getSqlSession(){ InputStream is = null; SqlSessionFactory sqlSessionFactory = null; SqlSession sqlSession = null; try { // 读取mybatis核心配置文件 is = Resources.getResourceAsStream("mybatis-config.xml"); // 解析流获取工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); // 获取sqlSession sqlSession = sqlSessionFactory.openSession(); } catch (IOException e) { e.printStackTrace(); } return sqlSession; } }
-
UserMapper接口
package com.itgaohe.mapper; import com.itgaohe.pojo.User; import org.apache.ibatis.annotations.Select; import java.util.List; public interface UserMapper { @Select("select * from tb_user") List<User> findAll(); }
-
mapper测试
import com.itgaohe.mapper.UserMapper; import com.itgaohe.pojo.User; import com.itgaohe.util.MybatisUtil; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserTest { @Test public void test(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAll(); System.out.println(all); } }
----------环境准备搞定了。---------
login
-
mapper
@Select("select * from tb_user where username = #{username} and password = #{password}") User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
-
service
package com.itgaohe.service; import com.itgaohe.pojo.User; public interface UserService { // login接口 User login(String username,String password); }
package com.itgaohe.service.impl;
import com.itgaohe.mapper.UserMapper;
import com.itgaohe.pojo.User;
import com.itgaohe.service.UserService;
import com.itgaohe.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
public class UserServiceImpl implements UserService {
public User login(String username, String password) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User u = mapper.findByUsernameAndPassword(username, password);
return u;
}
}
-
页面请求路径
-
loginServlet
package com.itgaohe.web; import com.itgaohe.mapper.UserMapper; import com.itgaohe.pojo.User; import com.itgaohe.service.impl.UserServiceImpl; import com.itgaohe.util.MybatisUtil; import org.apache.ibatis.session.SqlSession; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /* * // 1. 接收用户名和密码 // 2. 调用mapper查询数据库拿到结果 // 3. 判断User对象是否为null // 3.1 null 登录失败 // 3.2 登录成功 * */ @WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 乱码处理 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); // 1. 接收用户名和密码==页面中的name对应 String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println(username+"---"+password); // 2. 调用mapper查询数据库拿到结果 UserServiceImpl userService = new UserServiceImpl(); User u = userService.login(username,password); // 3. 判断User对象是否为null if (u == null){ // 3.1 null 登录失败 response.getWriter().write("登录失败:用户名或者密码错误!"); }else { // 3.2 登录成功 response.getWriter().write("登录成功!"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //一切请求由post解决 this.doPost(request, response); } }
register
-
mapper
// 添加用户 @Insert("insert into tb_user values(null,#{username},#{password})") int save(User user);
-
service
// 注册 int register(String username,String password);
public int register(String username, String password) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 封装数据
User u = new User();
u.setUsername(username);
u.setPassword(password);
int save = mapper.save(u);
// 事务提交
sqlSession.commit();
return save;
}
-
和页面对应好registerServlet
-
registerServlet
package com.itgaohe.web; import com.itgaohe.service.impl.UserServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * * 1. 接收数据 * 2. 调用业务层实现功能 * 3. 响应结果! * */ @WebServlet("/registerServlet") public class RegisterServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); // 1.接收数据 String username = request.getParameter("username"); String password = request.getParameter("password"); // 2. 调用业务层 UserServiceImpl userService = new UserServiceImpl(); int register = userService.register(username, password); // 3.响应 if(register >0){ response.getWriter().write("注册成功"); }else { response.getWriter().write("注册失败"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //一切请求由post解决 this.doPost(request, response); } }
测试
- 请求路径重点关注
- 请求参数名
能力目标
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
-
- 接收数据
-
- 调用业务层实现功能
-
- 响应结果!
*/
@WebServlet(“/registerServlet”)
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding(“utf-8”);
response.setContentType(“text/html;charset=utf-8”);
// 1.接收数据
String username = request.getParameter(“username”);
String password = request.getParameter(“password”);
// 2. 调用业务层
UserServiceImpl userService = new UserServiceImpl();
int register = userService.register(username, password);
// 3.响应
if(register >0){
response.getWriter().write(“注册成功”);
}else {
response.getWriter().write(“注册失败”);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//一切请求由post解决
this.doPost(request, response);
}
}
测试
- 请求路径重点关注
- 请求参数名
[外链图片转存中...(img-QrWk83d9-1719719080896)]
# 能力目标
- 登录注册功能!