7.Request(请求) 和 Response(响应)
Request(请求) 和 Response(响应)
笔记目录:(https://www.cnblogs.com/wenjie2000/p/16378441.html)
-
Request:获取请求数据
-
Response:设置响应数据
Request
Request继承体系
- Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法中
- 使用request对象,查阅JavaEE API文档的HttpServletRequest接口
Request获取请求数据
获取请求数据
-
请求数据分为3部分:
-
请求行:
GET /request-demo/req1?username=zhangsan HTTP/1.1
String getMethod()
:获取请求方式:GETString getContextPath()
:获取虚拟目录(项目访问路径):/request-demoStringBuffer getRequestURL()
:获取URL(统一资源定位符): http://localhost:8080/request-demo/req1String getRequestURI()
:获取URI(统一资源标识符):/request-demo/req1String getQueryString()
:获取请求参数(GET方式) :username=zhangsan&password=123
-
请求头:
User-Agent:Mozilla/5.0 Chrome/91.0.4472.106
String getHeader(String name)
:根据请求头名称,获取值
-
请求体:
username=superbaby&password=123
ServletlnputStream getlnputStream()
:获取字节输入流BufferedReader getReader()
:获取字符输入流
-
Request通用方式获取请求参数
-
请求参数获取方式:
-
GET方式:
String getQueryString()
-
POST方式
BufferedReader getReader()
-
思考:
GET请求方式和POST请求方式区别主要在于获取请求参数的方式不一样,是否可以提供一种统一获取请求参数的方式,从而统一doGet和doPost方法内的代码?
有了通用的请求参数的方式,那么就可以将任务都交给doPost和doGet方法中的一个处理,另一个只需要调用它就行了。
例如:先在doGet方法中实现原本doPost和doGet的所有功能。再在doPost方法中直接调用doGet(this. doGet(req,resp);
)
-
使用通用方式获取请求参数后,屏蔽了GET和POST的请求方式代码的不同,则代码可以定义为如下格式:
@WebServlet("/aaaa") public class Servlet4 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
可以使用Servlet模板创建Servlet更高效(设置模板:Setting-->File and Code Templates -->Other-->Web-->Servlet Annotated Class.java)
对模板修改成下面这样:
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end #parse("File Header.java") @javax.servlet.annotation.WebServlet("/${Entity_Name}") public class ${Class_Name} extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { this.doPost(request,response); } }
在项目目录中就可以根据模板创建Servlet了(要创建servlet的目录处 右键--> New-->CreateNewServlet-->设置类名-->创建成功)
请求参数中文乱码处理
tomcat10以下版本会出现POST乱码问题。tomcat8以下会出现GET乱码问题。(注意,如果使用tomcat-maven插件,属于是tomcat7)
-
请求参数如果存在中文数据,则会乱码
-
解决方案:
POST:设置输入流的编码(这里的字符流默认编码为iso-8859-1)
req.setCharacterEncoding("UTF-8");
通用方式(GET/POST)︰先编码,再解码(将乱码的内容转为字节,再转为utf-8)
String string = req.getParameter("aaaa"); new String(string.getBytes("ISO-8859-1"),"UTF-8");
Request请求转发
-
请求转发(forward):一种在服务器内部的资源跳转方式
-
实现方式:
req.getRequestDispatcher("资源B路径").forward(req,resp);//资源b路径格式为"/req2"或"req2"
-
请求转发资源间共享数据:使用Request对象(例:req.getAttribute("aaa"))
void setAttribute(String name, Object o)
:存储数据到request域中(放到request中一个专用于传递的集合)Object getAttribute(String name)
:根据key,获取值void removeAttribute(String name)
:根据key,删除该键值对
-
请求转发特点:
浏览器地址栏路径不发生变化
只能转发到当前服务器的内部资源
—次请求,可以在转发的资源间使用request共享数据
Response
Response继承体系
和request结构差不多
Response设置响应数据功能介绍
- 响应数据分为3部分:
- 响应行:
HTTP/1.1200 OK
void setStatus(int sc)
:设置响应状态码
- 响应头:
Content-Type: text/html
void setHeader(String name, String value)
:设置响应头键值对
- 响应体:
<html><head>head><body></body></html>
PrintWriter getWriter()
:获取字符输出流ServletOutputStream getOutputStream()
:获取字节输出流
- 响应行:
Response完成重定向
-
重定向(Redirect):—种资源跳转方式
-
实现方式:
resp.setStatus(302); resp.setHeader("location","资源B的路径");//路径可使用"/request-demo/resp2"或"resp2"的方式,建议用后者"resp2",能降低耦合度
以上两步为底层逻辑,也有简化的一步完成的实现方式:
response.sendRedirect("资源B的路径"); //路径可使用"/request-demo/resp2"或"resp2"的方式,建议用后者"resp2",能降低耦合度。 //如果强行要用前者,建议使用request.getContextPath()动态获取虚拟目录"/request-demo"
-
重定向特点:
浏览器地址栏路径发生变化
可以重定向到任意位置的资源(服务器内部、外部均可)
两次请求,不能在多个资源使用request共享数据
补充:路径问题
-
明确路径谁使用?
浏览器使用:需要加虚拟目录(项目访问路径)
服务端使用:不需要加虚拟目录
-
练习:
<a href='路径'> //加虚拟目录 <form action='路径'> //加虚拟目录 req.getRequestDispatcher("路径") //不加虚拟目录 resp.sendRedirect("路径") //加虚拟目录
Response响应字符数据
-
使用
-
通过Response对象获取字符输出流
PrintWriter writer = response.getWriter();
-
写数据
writer.write("aaaaaa一二三");
-
-
注意:
-
该流不需要关闭,随着响应结束,response对象销毁,由服务器关闭(手动关闭也不会报错)
-
中文数据乱码:原因通过Response获取的字符输出流默认编码:
ContentType:ISO-8859-1
response.setContentType("text/html;charset=utf-8");//如果utf-8也乱码,就用GBK //通过设置响应头中ContentType中的“text/html;”表示writer.write()的也就是响应内容的数据类型 //也同时设置charset=utf-8 代表编码 //注意,设置编码要在获取字符输出流之前
-
Response响应字节数据
-
使用
-
通过Response对象获取字节输出流
ServletOutputStream outputStream = response.getOutputStream();
-
写数据
outputStream.write(字节数据);
-
-
实例演示
//实现在网页上显示图片(字节数据) FileInputStream fis = new FileInputStream("C:\\Users\\ZWJ\\Desktop\\材料.png"); ServletOutputStream os = response.getOutputStream(); byte[] buff = new byte[1024]; int len; while ( (len = fis.read(buff)) != -1){ os.write(buff, 0, len); } fis.close();
-
IOUtils工具使用(方便数据流的拷贝)
-
导入坐标
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
-
使用(org.apache.commons.io.IOUtils)
//将输入流数据拷贝到输出流 IOUtils.copy(输入流,输出流);
使用上面的工具,就能简化之前的代码
FileInputStream fis = new FileInputStream("C:\\Users\\ZWJ\\Desktop\\材料.png"); ServletOutputStream os = response.getOutputStream(); /* byte[] buff = new byte[1024]; int len; while ( (len = fis.read(buff)) != -1){ os.write(buff, 0, len); }*/ IOUtils.copy(fis,os); fis.close();
-
练习
用户登录
实现网页登录功能(要使用到mybatis和servlet的知识)
-
流程说明:
- 用户填写用户名密码,提交到LoginServlet
- 在LoginServlet中使用MyBatis查询数据库,验证用户名密码是否正确
- 如果正确,响应“登录成功”,如果错误,响应“登录失败”
-
准备环境:
- 编写静态页面login.html
- 创建db1数据库,创建tb_user表,创建User实体类
- 导入MyBatis坐标,MySQL驱动坐标
- 创建mybatis-config.xml核心配置文件,UserMapper.xml映射文件,UserMapper接口
- 编写Servlet
自己先试着做,做不出来,再看代码
项目文件结构(圈出的部分内容我后面会给出)
pom.xml依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version><!--注意驱动版本,多半和我不一样-->
</dependency>
</dependencies>
前端页面(login.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录界面</title>
</head>
<body>
<form action="/login-web/loginServlet" method="post">
用户名:<input name="username">
密码:<input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
数据库表
-- 创建表
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;
User实体类(参考数据库表,我就不给了)
mybatis配置文件(mybatis-config.xml)
<?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.itwen.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://localhost:3306/db1"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<package name="com.itwen.mapper"/>
</mappers>
</configuration>
映射器接口(UserMapper.java)
package com.itwen.mapper;
import com.itwen.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
/**
* 根据用户名和密码查找用户对象
* @param username
* @param password
* @return
*/
@Select("select * from tb_user where username=#{un} and password=#{pd}")
public User selectUser(@Param("un") String username, @Param("pd") String password);
}
映射文件(UserMapper.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itwen.mapper.UserMapper">
</mapper>
LoginServlet.java
package com.itwen.web;
import com.itwen.mapper.UserMapper;
import com.itwen.pojo.User;
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 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;
import java.io.InputStream;
import java.io.PrintWriter;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.接收用户名密码
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username);
System.out.println(password);
//2.使用MyBatis查询(MyBatis部分学过)
//2.1 获取sqlSessionFactory对象(mybatis官网有)
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.selectUser(username, password);
//2.5释放资源
sqlSession.close();
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
System.out.println(user);
//3.判断user是否为空
if(user!=null){
//登录成功
writer.write("登陆成功");
}else {
//登录失败
writer.write("登录失败");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
用户注册
- 流程说明:
-
用户填写用户名、密码等信息,点击注册按钮,提交到RegisterServlet
-
在RegisterServlet中使用MyBatis保存数据
-
保存前,需要判断用户名是否已经存在:根据用户名查询数据库
代码和登录功能类似,我就不给了
代码优化
每个用户登录和注册都会创建SqlSessionFactory,类似的内容一多就会照成资源浪费。(他们可以共用一个SqlSessionFactory)
-
创建SqlSessionFactory 代码优化
需要时只需使用
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
获取工厂package com.itwen.util; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; 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; } }
思考:获取sqlSession的代码也是重复的,要不要也放到工具类中?
(SqlSession sqlSession = sqlSessionFactory.openSession();
)
所有用户和所有事务都共用同一个连接,会导致不能正常管理事务,并且多个用户多个功能之间产生影响