今天主要讲解的是:HTTP响应及HttpServletResponse对象。
将业务处理结果响应回浏览器
HTTP响应报文
HTTP是基于请求和响应的协议,一次请求对应一次响应。请求和响应的数据格式都遵从HTTP的超文本传输协议。与HTTP的请求一样,HTTP响应的数据也分为3部分:响应行 ,响应头 ,响应体 这3部分组成。所以,请求是通过浏览器将数据传送到服务器,而响应是将服务器处理的结果回送到浏览器。
响应行格式:协议/版本 状态码
常见状态码(这个一定要很熟悉,就靠状态码来判断哪里出问题了):响应头:
响应头也是用的键值对key:value,服务器基于响应头通知浏览器的行为。
常见的响应头:小结:
响应行:HTTP协议版本号 状态码 220 404 500 302 304
响应头:location content-type content-Disposition refresh
响应体:服务器将数据响应给浏览器。
HTTPServletResponse对象
服务器接受客户端的请求之后,如何将对应的数据响应给客户端呢?
一切皆对象,Servlet程序接受客户端请求之后,通过HttpServletResponse对象来完成客户端的响应。
通过该对象,将数据(可以是文本数据,也可以是二进制数据) 返回给客户端。
HTTPServletResponse对象的作用
HTTPServletResponse对象代表服务器响应,Tomcat创建HTTPServletResponse对象,并将该对象作为参数传递给servlet的service方法。
这个对象的作用:设置响应行、头、体。
介绍一下HTTPServletResponse对象常用的几个API。
响应行
响应行:协议/版本 如:HTTP/1.1 200 。这里边的信息,我们通常只会用到状态码,操作状态码的方法setStatus(int code);
- setStatus(int code):向浏览器发送状态码
//location+302 共同完成重定向 重定向到login.html图片
response.setHeader("location", "login.html");
response.setStatus(302);
//可以使用一行代码替换
response.sendRedirect("login.html");
常用状态码:
200响应成功
404资源路径错误或资源被删除
500服务器内部错误
302重定向
响应头
- setHeader(String name,String value)设置响应头信息
给浏览器设置响应头:Content-Disposition 告诉浏览器以附件的形式打开这个文件
response.setHeader("Content-Disposition","attachment;filename="+fileName);
实践:定时刷新页面
- 访问到Servlet类三秒后跳转到百度;
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//实现定时刷新功能 参数1 关键字 参数2 url
resp.setHeader("refresh","3;url=http://www.baidu.com");
}
}
- content-type设置响应数据的类型
@WebServlet("/type")
public class TypeServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//使用content-type
//设置响应数据的类型(MIME类型)和编码格式 例如text/html;charset=UTF-8
//告诉浏览器不要解析html标签 text/plain
resp.setContentType("text/plain");
resp.getWriter().println("<h1>java</h1>");
}
}
处理响应乱码
产生的原因:
服务器如果需要响应给浏览器中文数据,tomcat使用getWriter()输出字符时,对于中文需要进行编码处理,而tomcat8 默认编码是ISO-8859-1,该码表是不支持中文编码的。所以响应给浏览器,出现乱码。
响应乱码解决方案:
因为HttpServlerResponse实现类是由tomcat服务器提供的。所以服务器在向浏览器输出数据之前,对于中文要采用支持中文的编码进行编码,通过response.setContentType(“text/html;charset=utf-8”); :
- 设置响应的数据类型(mime-type);
- 设置响应的数据编码(utf-8);
处理响应乱码的API:
- response.setHeader(“content-type”,“text/html;charset=utf-8”);设置响应头
- response.setContentType(“text/html;charset=utf-8”);简写方式
注意:
- 开发中通常使用response.setContentType(“text/html;charset=utf-8”);来处理响应乱码;
- 处理响应乱码必须书写在响应数据之前才起作用;
@WebServlet("/luanma")
public class TypeServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决中文乱码 text/html;charset(字符集)=utf-8
req.setCharacterEncoding("utf-8");
//使用content-type
//设置响应数据的类型(MIME类型)和编码格式 例如text/html;charset=UTF-8
//告诉浏览器不要解析html标签 text/plain
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println("我真帅");
}
}
重定向
重定向API
-
response.setStatus(302);
-
response.setHeader(“location”, “/otherServlet”);
通过设置响应状态码和响应头实现重定向 -
常用这个:response.sendRedirect("/otherServlet");重定向的简写方式
实践:通过设置响应头location和状态码302完成重定向。
//location+302 共同完成重定向
response.setHeader("location", "url");
response.setStatus(302);
@WebServlet("/two1")
public class RedirectTwoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用重定向API
response.sendRedirect("/teacher");
}
}
@WebServlet("/teacher")
public class TeacherServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("问题解决了。。");
}
}
转发与重定向的区别转发与重定向怎么选择
- 如果需要共享request域,必须使用转发;
- 如果需要将浏览器地址栏地址变成第二次请求的地址,必须选择重定向。
实践:写个登录案例
需求:
登录失败,将登录信息转发到一个专门处理登录结果的servlet。由这个Servlet给浏览器响应信息;
登录成功,重定向到首页。
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//登陆业务逻辑的处理
User user = new User(username,password);
UserService service = new UserService();
// service.login(username,password);
boolean result = service.login(user);
//获取到登陆结果
if (result){
//登陆成功 需要携带数据 使用重定向 跳转success.html
//url发生变化
// request.getRequestDispatcher("/success.html").forward(request,response);
response.sendRedirect("/success.html");
}else {
//登陆失败 --》让用户接着登陆 提升用户用户名或者密码错误
// 使用转发 跳转到error.html
// request.getRequestDispatcher("/error.html").forward(request,response);
//解决中文乱码问题
response.setContentType("text/html;charset=utf-8");
//进行标签拼接
//代码原子性思想
//request域对象存储登陆失败的信息
request.setAttribute("msg","用户名或者密码错误");
//携带数据 使用转发
request.getRequestDispatcher("/loginerror").forward(request,response);
}
}
}
@WebServlet("/loginerror")
public class LoginErrorServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决用户名或者密码错误业务
String msg = (String) request.getAttribute("msg");
response.getWriter().println("<html>");
response.getWriter().println("<body>");
response.getWriter().println("<form action='/loginServlet' method='post'>");
response.getWriter().println("<font color='red'>"+msg+"</font>");
response.getWriter().println("用户名:<input type='text' name='username'>");
response.getWriter().println("密码:<input type='password' name='password'>");
response.getWriter().println("<input type='submit' value='提交'>");
response.getWriter().println("</form>");
response.getWriter().println("</body>");
response.getWriter().println("</html>");
}
}
总登录流程响应体:
-
ServletOutputStream getOutputStream()用于向浏览器输出二进制数据。【字节流–文件下载】
-
java.io.PrintWriter getWriter()用于向浏览器输出字符数据。【字符流–给浏览器响应数据】
@WebServlet(name = "ResponseDemo1",urlPatterns = "/demo1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* response.getWriter()方法向浏览器打印数据:
*
* 方式一:response.getWriter().print(): 可以打印任意任类型的数据
*
* */
char[] arr = {'a','b'};
response.getWriter().println("hello");
response.getWriter().println('a');
response.getWriter().println(65);
response.getWriter().println(true);
response.getWriter().println(arr);
}
}
实践:文件下载功能
ServletContext对象介绍
ServletContext对象:servlet容器在启动时会加载web应用,并为每个web应用创建唯一的servlet context对象,可以把ServletContext看成是一个Web应用的服务器端组件的共享内存,在ServletContext中可以存放共享数据。ServletContext对象是真正的一个全局对象,凡是web容器中的Servlet都可以访问。
常用API
-
setAttribute(String name,Object object):向ServletContext中存数据
-
getAttribute(String name):从ServletContext中取数据
-
removeAttribute(name):从ServletContext中移除数据
-
String getRealPath(String path):返回资源文件在服务器文件系统上的真实路径(文件的绝对路径)
-
getServletContext().getMimeType(fileName):获取服务器中文件类型
ServletContext对象,tomcat为每一个web项目单独创建的一个上下文(知上知下贯穿全文)对象。就有功能:
1.可以在多个servlet之间共享数据
存放:setAttribute()
获得:getAttribute()
删除:removeAttribute()
2.可以获得当前WEB项目中的指定资源(文件)
String path = getRealPath( String string);
3.可以进行整个web项目初始化数据设置
String parameter = getInitPatameter(String parameter);
在web.xml可以给整个web项目配置初始化参数
获取服务器中每个文件的路径
@WebServlet("/roadServlet")
public class RoadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String realPath = getServletContext().getRealPath("/1.jgp");
//realPath = D:\code\demo03ajax\out\artifacts\web_war_exploded\1.jgp
System.out.println("realPath = " + realPath);
}
}
需求: 通常情况下,浏览器访问网站时,往往需要下载服务器资源。资源可能是文本数据或者二进制数据。
例如: 图片下载、文件下载、电影下载等。那么此时我们该如何编写代码,让客户通过浏览器获取服务器上的资源呢?
解决方案:
1.手动给浏览器设置响应头(content-disposition)告诉浏览器以文件下载的方式打开这个文件;
2.当用户浏览器访问Servlet时,我们可以通过HttpServletResponse对象提供的getOutputStream()输出流,将服务器磁盘中的资源基于流输出给浏览器。
实现文件下载功能
需求:用户点击页面的连接,浏览器开始下载文件
- 创建一个页面,展示所有被下载的文件的连接;
- 链接要下载的文件名称,发送给服务器的Servlet,让servlet进行处理;
- 服务器加载文件资源;
- 提示浏览器,以下载的方式,获取服务器资源;
- 使用IO的方式,将文件数据输出到浏览器(response.getOutputStream)
页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件下载</title>
</head>
<body>
<a href="/download?filename=1.jpg">1.jpg</a><br>
<a href="/download?filename=2.txt">2.txt</a><br>
<a href="/download?filename=3.zip">3.zip</a><br>
<a href="/download?filename=Sublime.exe">Sublime.exe</a><br>
</body>
</html>
编写servlet处理文件下载逻辑
要点:
获取当前项目中资源的物理路径;
String realPath = getServletContext().getRealPath("/WEB-INF/");
获取文件的mimeType;
String mimeType = getServletContext().getMimeType(fileName);
文件下载:
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//进入下载代码的实现
//中文乱码解决
request.setCharacterEncoding("utf-8");
//获取文件名
String filename = request.getParameter("filename");
//获取servletContext
ServletContext servletContext = request.getServletContext();
//获取文件路径
String realPath = servletContext.getRealPath("/WEB-INF/" + filename);
System.out.println(realPath);
//创建file对象
File file = new File(realPath);
if (file.exists()){
//存在 可以进行下载
//解决两个头
//获取文件类型
String mimeType = servletContext.getMimeType(filename);
//告诉浏览器文件类型
response.setContentType(mimeType);
//告诉浏览器以附件的形式下载 不要解析图片
// response.setHeader("Content-Disposition","attachment;filename="+filename);
response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(filename,"utf-8"));
//2个流问题 流拷贝
//获取2个流
ServletOutputStream outputStream = response.getOutputStream();//输出流
//获取输入流
FileInputStream inputStream = new FileInputStream(file);
//流拷贝
int len = 0;
byte[] arr = new byte[1024];
while ((len = inputStream.read(arr))!=-1){
outputStream.write(arr,0,len);
}
//关闭流
outputStream.close();
inputStream.close();
}
}
}
文件下载:两个头+流拷贝
- 设置响应头:Content-Disposition告诉浏览器以附件的形式来处理文件;
- 设置响应头:response.setContentType(mimeType)告诉浏览器下载的文件类型;
- 流拷贝:
将文件读取到流里:FileInputStream fin=new FileInputStream(file);
将流写给浏览器:response.getOutputStream().write(arr,0,len);