HTTP
超文本传输协议:定义客户端和服务器之间传输数据的格式
特点:
- 基于TCP/IP的高级协议
- 默认端口号:80
- 基于请求/响应模型:一次请求对应一次响应
- 无状态的:每次请求之间相互独立
历史版本:
- 1.0:每次请求响应都会建立新的连接(一个网页需要多个请求)
- 1.1:复用连接
传输过程:
- tomcat根据请求url中的资源路径,根据配置文件找到Servlet实现类,并创建对象;
- tomcat创建request和response对象,requeset对象中封装请求消息;
- tomcat调用service方法
- 程序员在service中,根据请求消息,设置相应数据
- tomcat将response返给浏览器
Request
请求消息数据格式:
-
请求行
请求方式 请求url 请求协议/版本
如:
GET /day04/demo1?name=zhangsan HTTP/1.1
-
请求头
请求头名称:请求头值
(多个请求头)- User-Agent:浏览器版本等信息
- Referer:请求来自哪个网站
-
请求空行:就是一个空行
-
请求体
- GET没有,POST有
request类
ServletRequest 接口
HttpServletrequest 接口,继承上面的接口
org.apache.catalina.connector.RequestFacade 类,由Tomcat实现,继承上面接口
所以,Tomcat实现request类
request对象
常用方法
- 获取请求行数据(几个常用的)
- 获取虚拟目录:
String getContextPath()
- 获取URI:
String getRequestURI()
如/day04/demo
- 获取URL:
StringBuffer getRequestURL()
如http://localhost/day04/demo
- 获取虚拟目录:
- 获取请求头数据
String getHeader(String name)
根据请求头名称获得值
- 获取请求体数据 (POST请求方式)
- 获取流对象:字符流
getReader()
字节流getInputStream()
- 从流对象中拿数据
- 获取流对象:字符流
其他方法
-
获取请求参数通用方法 (GET和POST都可以)
String getParameter(String name)
更根据参数名称获取参数值String[] getParameterValues(String name)
更根据参数名称获取参数组getParameterNames()
获取所有请求的参数名称Map<String String[]> getParameterNames()
获取所有请求的参数map集合- POST会遇到中文乱码,需要先设置
req.setCharacterEncoding("utf-8")
-
请求转发:服务器内部的资源跳转方式(如Demo1转到Demo2)
req.getRequestDispatcher("/demo2").forward(req, resp);
特点:浏览器地址栏没有发生变化;只能转发到当前服务器内部资源。
-
共享数据
- 域对象:一个有作用范围的对象,可以在范围内共享数据
- request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
setAttribute(String name, Object obj)
存储数据Object getAttrbute(String name)
通过键获取值removeAttribute(String name)
通过键移除键值对
-
获取ServletContext
ServletContext getServletContext()
后面再讲
案例
用户登录的业务逻辑:
- 创建项目,导入login.html
- 创建数据库中的user表
- 创建User类
- 创建JDBCUtils工具类(获取连接池,获取链接对象)
- 创建UserDao类,实现login方法(使用JDBCTemplate执行sql查询)
- 创建loginServlet类:
- 获取请求参数 (username, password)
- 封装成User对象
- 调用UserDao的login方法 (传入User对象,执行sql语句,判断)
- 判断是否登录成功
BeanUtils工具类:简化 请求参数 的封装
- 导入jar包
commons-beanutils-1.8.0.jar
- User类必须符合JavaBean标准(JavaBean是定义一个Java类的标准的要求,可百度搜索)
BeanUtils代码:(Servlet中)
// 设置请求数据编码
req.setCharacterEncoding("UTF-8");
// 获取提交数据
Map<String, String[]> map = req.getParameterMap();
// 创建对象
User loginUser = new User();
// 封装对象
BeanUtils.populate(loginUser, map); // try-catch捕获
// 登录判断
UserDao dao = new UserDao();
User user = dao.login(loginUser);
if (user == null) {
System.out.println("登录失败");
} else {
System.out.println("登录成功");
}
案例代码
文件目录结构:
--项目/模块
--src/包
druid-properties // 连接池配置文件
domain/User
dao/UserDao
util/JDBCUtils
servlet/ServletDemo
--web
--WEB-INF
lib // jar包,需要右键add as libaray
xxx.html
xx.jsp
login.html
<form action="./demo" method="post">
username: <input type="text" name="username"> <br>
password: <input type="password" name="password"> <br>
<input type="submit" value="submit">
</form>
User略 (JavaBean标准类)
JDBCUtils略 (参考JDBC中的Druid连接池,一模一样)
UserDao类:
public class UserDao {
// 声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
public User login(User loginUser) {
try {
String sql = "select * from user where username=? and password=?";
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();
return null;
}
}
}
Servlet类,参考上面BeanUtils
Response
响应消息格式:
- 响应行:
协议/版本 响应状态码 状态码描述
(HTTP/1.1 200 OK
)- 响应状态码:都是3位数,分为5类
- 1xx 服务器收到消息,但没有接受完成
- 2xx 成功200
- 3xx 重定向 (302重定向 304访问缓存)
- 4xx 客户端错误 (404请求路径错误 405请求方法没有对应的doXx方法)
- 5xx 服务器错误 (500服务内异常)
- 响应状态码:都是3位数,分为5类
- 响应头
头名称:值
- Content-type:响应数据格式(html),以及编码格式
- Content-disposition:以什么格式打开响应数据(当前页面内打开、附件下载等)
- 响应空行:空行
- 响应体:HTML代码/图片的编码
response对象
常用方法
- 设置响应行:
setStatus(int sc)
- 设置响应头:
setHeader(String name, String value)
- 设置响应体:
- 获取输出流:字符输出流
getWriter()
、字节输出流getOutputStream()
- 使用输出流,将数据输出到客户端
- 获取输出流:字符输出流
其他方法
-
重定向:
resp.sendRedirect("/day04/ServletDemo2")
- 重定向特点:地址栏发生变化,属于两次请求,可以访问其他站点资源,不能共享数据
- 转发特点:地址栏不变,是一次请求,只能访问当前站点资源,能共享数据
- 可以获取虚拟目录:
String req.getContextPath()
-
告诉浏览器编码方式
resp.setContentType("text/html;charset=utf-8");
-
字符流
resp.setContentType("text/html;charset=utf-8"); PrintWriter pw = resp.getWriter(); pw.write("你好 hello");
-
字节流
resp.setContentType("text/html;charset=utf-8"); ServletOutputStream os = resp.getOutputStream(); os.write("你好".getBytes("utf-8"));
ServletContext
概念:代表整个web应用,可以和程序的服务器交互
获取ServletContext对象:
req.getServletContext();
this.getServletContext();
(建议使用)
功能:
-
获取MIME类型
-
MIME类型:互联网通信定义的一种文件数据类型
-
MIME格式:
大类型/小类型
, 如text/html
-
ServletContext context1 = req.getServletContext(); String filename = "a.jpg"; System.out.println(context1.getMimeType(filename)); // 输出image/jpeg
-
-
域对象:共享数据:所有用户,所有数据
setAttribute(String, Object);
getAttribute(String);
removeAttribute(String)
-
获取文件在服务器中的路径
ServletContext c = this.getServletContext(); String path = c.getRealPath("/b.txt"); // web目录下的资源路径 String path2 = c.getContextPath("/WEB-INF/classes/a.txt"); // src目录下的资源路径
下载功能
现在浏览器比较高级,一些文件可以自动转到下载功能。
对于任何文件,如何实现下载功能(让浏览器下载):
- 使用响应头设置资源的打开方式:
content-disposition:attachment;filename=xxx
(以附件形式打开)
实现:
- 超链接指向servlet (传递文件名);
- servlet获取文件名;加载文件进内存;指定response响应头;将文件写到response输出流中。
代码:
-
html页面:
- 图片放在web/img/687.png,必须有img文件夹
- servlet是虚拟目录,demo是servlet资源路径
<a href="/servlet/demo?filename=1.png">图片下载</a>
-
servlet编程:
// 获取参数 String filename = req.getParameter("filename"); // 找到资源路径 ServletContext c = this.getServletContext(); String realPath = c.getRealPath("/img/" + filename); // 设置响应头(文件类型) String mimeType = c.getMimeType(filename); resp.setHeader("content-type", mimeType); // 设置响应头(浏览器打开方式) resp.setHeader("content-disposition", "attachment;filename="+filename); // 字节流加载 FileInputStream is = new FileInputStream(realPath); // 数据输出 ServletOutputStream os = resp.getOutputStream(); byte[] buff = new byte[2014 * 8]; int len = 0; while ((len = is.read(buff)) != -1) { os.write(buff, 0, len); } is.close();
中文乱码
刚才下载的文件,如果是中文,不同浏览器显示不一样的编码。
解决思路:
- 获取客户端使用的浏览器版本信息
- 根据不同的版本,设置不同的编码方式
代码:
- 将DownLoadUtils.java文件(工具类)放在utils工具包
- servlet中编码
String agent = req.getHeader("user-agent");
filename = DownLoadUtils.getFileName(agent, filename);