Request&Response
Request:获取请求数据
Response:设置响应数据
//初识Request&Response
@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//使用request对象,获取请求数据
String name = req.getParameter("name");
//使用response对象,设置响应数据
resp.setHeader("Content-Type", "text/html;charset=utf-8");
resp.getWriter().write("<h1>Hello " + name + "</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
}
}
Request对象
Request继承体系
ServletRequest:Java提供的请求对象根接口
HttpServletRequest:Java提供的对HTTP协议封装的请求对象接口
RequestFacade:Tomcat定义的实现类
- Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法中
- 使用request对象,查阅JavaEE API文档的HttpServletRequest接口即可
Request获取请求数据
获取请求数据
1. 请求行
String getMethod() //获取请求方式
String getContextPath() //获取虚拟目录(项目访问路径)
StringBuffer getRequestURL() //获取URL(统一资源定位符)
String getRequestURI() //获取URI(统一资源标识符)
String getQueryString() //获取请求参数(GET方式)
@WebServlet("/req1")
public class RequestDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String getMethod() //获取请求方式
String method = req.getMethod();
System.out.println("请求方式:" + method);
// String getContextPath() //获取虚拟目录(项目访问路径)
String contextPath = req.getContextPath();
System.out.println("虚拟目录:" + contextPath);
// StringBuffer getRequestURL() //获取URL(统一资源定位符)
StringBuffer requestURL = req.getRequestURL();
System.out.println("URL:" + requestURL.toString());
// String getRequestURI() //获取URI(统一资源标识符)
String requestURI = req.getRequestURI();
System.out.println("URI:" + requestURI);
// String getQueryString() //获取请求参数(GET方式)
String queryString = req.getQueryString();
System.out.println("请求参数:" + queryString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
2. 请求头
String getHeader(String name) //根据请求头名称获取值
@WebServlet("/req1")
public class RequestDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String getHeader(String name) //根据请求头名称获取值
String header = req.getHeader("User-Agent");
System.out.println(header);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
3. 请求体
只有post请求才有请求体
ServletInputStream getInputStream() //获取字节输入流
BufferedReader getReader() //获取字符输入流
@WebServlet("/req1")
public class RequestDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取post请求体:请求参数
//1. 获取字符输入流
BufferedReader reader = req.getReader();
//2. 读取数据
String line = reader.readLine();
System.out.println(line);
}
}
通用方式获取请求数据
GET方式:String getQueryString()
Post方式:BufferedReader getReader()
Map<String, String[]> getParameterMap() //获取所有参数Map集合
String[] getParameterValues(String name) //根据名称获取参数值(数组)
String getParameter(String name) //根据名称获取参数值(单个值)
@WebServlet("/req1")
public class RequestDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//get请求逻辑
//System.out.println("get...");
//1. 获取所有参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
System.out.print(key + ":");
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
//2. 根据key获取参数值(数组)
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
//3. 根据key获取参数值(单个)
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username + ":" + password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//post请求逻辑
//System.out.println("post...");
this.doGet(req, resp);
}
}
请求参数中文乱码处理
解决方案:
post:
原因:getReader()获取字符输入流,Tomcat默认获取流的编码为ISO-8859-1
解决:设置输入流的编码 req.setCharacterEncoding("UTF-8");
get:
原因:浏览器将中文进行UTF-8格式URL编码,Tomcat再进行URL解码
而Tomcat默认解码格式为ISO-8859-1
解决:1. 先对乱码数据进行编码,转为字节数组
2. 将字节数组转为字符串
注:Tomcat8.0版本之后,已将GET请求乱码问题解决,设置默认编码方式为UTF-8
URL编码:
- 将字符串按照编码方式转为二进制
- 每个字节转为2个16进制数并在前边加上%
//模拟get方式编码解码
public class URLDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String name = "张三";
//1. 模拟浏览器编码
String encode = URLEncoder.encode(name, "utf-8");
System.out.println(encode);
//2. 模拟Tomcat解码
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 + " ");
}
//4. 将字节数组转为字符串
String s = new String(bytes, "UTF-8");
System.out.println(s);
}
}
@WebServlet("/req1")
public class RequestDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 解决乱码:Post方式
req.setCharacterEncoding("UTF-8");
//测试中文乱码
String username = req.getParameter("username");
System.out.println(username);
//解决乱码:Get方式与Post方式通用
//3.1 先对乱码数据进行编码,转为字节数组
byte[] bytes = username.getBytes("ISO-8859-1");
//3.2 再对字节数组进行解码,转为字符串
username = new String(bytes, "UTF-8");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
Request请求转发
请求转发(forward):一种在服务器内部的资源跳转方式
实现方式: req.getRequestDispatcher("资源路径").forward(req, resp);
请求转发资源间共享数据:使用Request对象
1. void setAttribute(String name, Object o):设置数据到request域中
2. Object getAttribute(String name):根据key获取值
3. void removeAttribute(String name):根据key删除该键值对
//ForwardDemoA.java
@WebServlet("/fdA")
public class ForwardDemoA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ForwardDemoA");
//存储数据
req.setAttribute("name", "tom");
//请求转发
req.getRequestDispatcher("/fdB").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
//ForwardDemoB.java
@WebServlet("/fdB")
public class ForwardDemoB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ForwardDemoB");
//获取数据
Object name = req.getAttribute("name");
System.out.println("name:" + name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
请求转发的特点
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器的内部资源
- 一次请求,可以在转发的资源间使用request共享数据
Response对象
Response继承体系
ServletResponse:Java提供的请求对象根接口
HttpServletResponse:Java提供的对HTTP协议封装的请求对象
ResponseFacade:Tomcat定义的实现类
Response设置响应数据功能介绍
1. 响应行
void setStatus(int sc) //设置响应状态码
2. 响应头
void setHeader(String name, String value) //设置响应头键值对
3. 响应体
PrintWriter getWriter() //获取字符输出流
ServletOutputStream getOutputStream() //获取字节输出流
Response完成重定向
重定向(Redirect):一种资源跳转方式
实现方法:
resp.setStatus(302);
resp.setHeader("location", "资源路径");
//RedirectDemoA.java
@WebServlet("/RDA")
public class RedirectDemoA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("RedirectDemoA");
//重定向
//1. 设置响应状态码
// resp.setStatus(302);
//2. 设置响应头
// resp.setHeader("Location", "/RDB");
//简化方式实现重定向
resp.sendRedirect("/RDB");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
//RedirectDemoB.java
@WebServlet("/RDB")
public class RedirectDemoB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("RedirectDemoB");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
重定向特点
- 浏览器地址栏路径发生变化
- 可以重定向到任意位置的资源(服务器内部、外部均可)
- 两次请求,不能在多个资源使用request共享数据
路径问题
明确路径由谁使用:
浏览器使用:需要加虚拟目录
服务端使用:不需要加虚拟目录
@WebServlet("/RDA")
public class RedirectDemoA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("RedirectDemoA");
//重定向
//动态获取虚拟目录
String contextPath = req.getContextPath();
//简化方式实现重定向
resp.sendRedirect(contextPath + "/RDB");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
Response响应字符数据
使用:
1. 通过Response对象获取字符输出流
PrintWriter writer = resp.getWriter();
2. 写数据
writer.write("aaa");
@WebServlet("/resp")
public class ResponseDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置
resp.setContentType("text/html;charset=utf-8");
//1. 获取字符输出流
PrintWriter writer = resp.getWriter();
//resp.setHeader("Content-Type", "text/html;charset=utf-8");
//2. 写数据
writer.write("hello world");
writer.write("<h1>hello world</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
注意:
该流无需关闭,随着响应结束,response对象销毁,由服务器关闭
中文数据乱码原因:通过Response获取的字符输出流默认编码为ISO-8859-1
Response响应字节数据
使用:
1. 通过Response对象获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();
2. 写数据
outputStream.write(字节数据);
@WebServlet("/resp")
public class ResponseDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 读取文件
FileInputStream fileInputStream = new FileInputStream("C://a.png");
//2. 获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();
//3. 完成流的copy
// byte[] bytes = new byte[1024];
// int len = 0;
// while ((len = fileInputStream.read(bytes)) != -1) {
// outputStream.write(bytes, 0, len);
// }
//使用IOUtils完成流的copy(需要在pom.xml中导入commons-io坐标)
IOUtils.copy(fileInputStream, outputStream);
//4. 关闭流
fileInputStream.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
案例
环境准备:
- 创建web项目(详情查看Web笔记)
- 在webapp目录下创建静态页面资源
- 创建数据库和对应的user表
- 导入MyBatis坐标,Mysql驱动坐标(详情查看MyBatis笔记)
- 创建mybatis-config.xml核心配置文件,UserMapper.xml映射文件,UserMapper接口(详情查看MyBatis笔记)
用户登录
流程:
1. 用户填写用户名和密码,提交到LoginServlet
2. 在LoginServlet中使用MyBatis查询数据库,返回User对象,验证用户名密码是否正确
3. 如果User对象不为null,则账号密码正确,响应登录成功,反之错误,响应登录失败
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 接收用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
//2. 调用MyBatis完成查询
//2.1 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream (resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.2 获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//2.3 获取Mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//2.4 调用方法
User user = mapper.select(username, password);
//2.5 关闭资源
sqlSession.close();
//获取字符输出流,并设置编码格式
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
//3. 判断结果
if (user != null) {
//登录成功
writer.write("<h1>登录成功</h1>");
}else{
//登录失败
writer.write("<h1>登录失败</h1>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
用户注册
流程:
1. 用户填写用户名、密码等信息,点击注册按钮,提交到RegisterServlet
2. 在RegisterServlet中使用MyBatis保存数据
3. 保存前,需要判断用户名是否已经存在:根据用户名查询数据库
@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 接收用户数据
String username = req.getParameter("username");
String password = req.getParameter("password");
//封装用户对象
User user = new User();
user.setUsername(username);
user.setPassword(password);
//2. 调用mapper,根据用户名查询用户对象
//2.1 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream (resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.2 获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//2.3 获取Mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//2.4 调用方法
User user1 = mapper.selectByUsername(username);
//3. 判断用户是否存在
if (user1 == null) {
//4. 如果不存在,调用mapper,插入用户
mapper.add(user);
//提交事务
sqlSession.commit();
sqlSession.close();
}else{
//5. 如果存在,提示用户已经存在
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("用户已经存在");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
代码优化
在创建SqlSessionFactory代码中有以下问题:
1. 代码重复
2. SqlSessionFactory工厂只创建一次,不要重复创建
解决方法:
1. 工具类
2. 静态代码块
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//静态代码块随着类的加载而自动执行,并且只执行一次
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream (resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
//2.1 获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
sqlSession对象不能够让所有用户共享
因为sqlSession代表用户和数据库的连接
不能够让所有用户共享一个连接
附:案例资源