HttpServlet抽象类
HttpServlet抽象类是继承于GenericServlet抽象类而来的。使用HttpServlet抽象类时,还需要借助分别代表Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象。
request和response对象
Tomcat收到客户端的http请求,会针对每一次请求,分别创建一个代表请求的request对象、和代表响应的response对象
request对象代表http请求:获取浏览器提交过来的数据,找request对象。
response对象代表http响应:向浏览器输出数据,找response对象。
HttpServletResponse对象
HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
常见应用
1、使用OutputStream流向客户端浏览器输出中文数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class ResponseDemo01 extends HttpServlet {
private static final long serialVersionUID = 4312868947607181532L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { outputChineseByOutputStream(response); }
public void outputChineseByOutputStream(HttpServletResponse response) throws IOException{
String data = "中国"; OutputStream outputStream = response.getOutputStream(); response.setHeader("content-type", "text/html;charset=UTF-8");
byte[] dataByteArr = data.getBytes("UTF-8"); outputStream.write(dataByteArr); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
}
|
2、使用PrintWriter流向客户端浏览器输出中文数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class ResponseDemo01 extends HttpServlet {
private static final long serialVersionUID = 4312868947607181532L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { outputChineseByPrintWriter(response); }
public void outputChineseByPrintWriter(HttpServletResponse response) throws IOException{ String data = "中国";
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>"); out.write(data); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
3、使用OutputStream或者PrintWriter向客户端浏览器输出数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class ResponseDemo01 extends HttpServlet {
private static final long serialVersionUID = 4312868947607181532L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
outputOneByOutputStream(response);
}
public void outputOneByOutputStream(HttpServletResponse response) throws IOException{ response.setHeader("content-type", "text/html;charset=UTF-8"); OutputStream outputStream = response.getOutputStream(); outputStream.write("使用OutputStream流输出数字1:".getBytes("UTF-8")); outputStream.write((1+"").getBytes()); }
}
|
4、文件下载
文件下载功能的实现思路:
1.获取要下载的文件的绝对路径
2.获取要下载的文件名
3.设置content-disposition响应头控制浏览器以下载的形式打开文件
4.获取要下载的文件输入流
5.创建数据缓冲区
6.通过response对象获取OutputStream流
7.将FileInputStream流写入到buffer缓冲区
8.使用OutputStream将缓冲区的数据输出到客户端浏览器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { downloadFileByOutputStream(response); }
private void downloadFileByOutputStream(HttpServletResponse response) throws FileNotFoundException, IOException { String realPath = this.getServletContext().getRealPath("/download/1.JPG"); String fileName = realPath.substring(realPath.lastIndexOf("\\")+1); response.setHeader("content-disposition", "attachment;filename="+fileName); InputStream in = new FileInputStream(realPath); int len = 0; byte[] buffer = new byte[1024]; OutputStream out = response.getOutputStream(); while ((len = in.read(buffer)) > 0) { out.write(buffer,0,len); } in.close(); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
当下载中文文件时,文件名需要经过URL编码,否则会出现文件名乱码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { downloadChineseFileByOutputStream(response); }
private void downloadChineseFileByOutputStream(HttpServletResponse response) throws FileNotFoundException, IOException { String realPath = this.getServletContext().getRealPath("/download/张家界国家森林公园.JPG"); String fileName = realPath.substring(realPath.lastIndexOf("\\")+1); response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8")); InputStream in = new FileInputStream(realPath); int len = 0; byte[] buffer = new byte[1024]; OutputStream out = response.getOutputStream(); while ((len = in.read(buffer)) > 0) { out.write(buffer,0,len); } in.close(); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
文件下载注意事项:编写文件下载功能时推荐使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。
5、生成验证码
生成图片需要使用到bufferedImage类
javaweb学习总结(九)—— 通过Servlet生成验证码图片 - 孤傲苍狼 - 博客园 (cnblogs.com)
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class ResponseDemo03 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("refresh", "5"); BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D)image.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, 80, 20); g.setColor(Color.BLUE); g.setFont(new Font(null, Font.BOLD, 20)); g.drawString(makeNum(), 0, 20); response.setContentType("image/jpeg"); response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); ImageIO.write(image, "jpg", response.getOutputStream()); }
private String makeNum(){ Random random = new Random(); String num = random.nextInt(9999999)+""; StringBuffer sb = new StringBuffer(); for (int i = 0; i < 7-num.length(); i++) { sb.append("0"); } num = sb.toString()+num; return num; }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
}
|
6、设置响应头控制浏览器行为
1 2 3
| response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache");
|
7、通过response实现请求重定向
请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。
应用场景:用户登陆,用户首先访问登录页面,登录成功后,就会跳转到某个页面,这个过程就是一个请求重定向的过程
实现方式:response.sendRedirect(String location),即调用response对象的sendRedirect方法实现请求重定向
sendRedirect内部的实现原理:使用response设置302状态码和设置location响应头实现重定向
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class ResponseDemo04 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
8、数据压缩
GZIP压缩类可对数据进行压缩:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| GZIPOutputStream gzipOutputStream = new GZIPOutputStream();
gzipOutputStream.write();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new ByteArrayOutputStream()); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
gzipOutputStream.write(ss.getBytes());
gzipOutputStream.close();
byte[] bytes = byteArrayOutputStream.toByteArray();
response.getOutputStream().write(bytes);
response.setHeader("Content-Encoding","gzip");
response.getOutputStream().write(bytes);
|
输出流——getWriter()和getOutputStream()
- getWriter()和getOutputStream()两个方法不能同时调用。
- Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
- Servlet的serice()方法结束后【也就是doPost()或者doGet()结束后】,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象.
HttpServletRequest对象
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
简单来说,要得到浏览器信息,就找HttpServletRequest对象
request对象作为一个域对象(Map容器)使用时,主要是通过以下的四个方法来操作
- setAttribute(String name,Object o)方法,将数据作为request对象的一个属性存放到request对象中,例如:request.setAttribute(“data”, data);
- getAttribute(String name)方法,获取request对象的name属性的属性值,例如:request.getAttribute(“data”)
- removeAttribute(String name)方法,移除request对象的name属性,例如:request.removeAttribute(“data”)
- getAttributeNames方法,获取request对象的所有属性名,返回的是一个,例如:Enumeration attrNames = request.getAttributeNames();
常见api
获得客户机【浏览器】信息
- getRequestURL方法返回客户端发出请求时的完整URL。
- getRequestURI方法返回请求行中的资源名部分。
- getQueryString 方法返回请求行中的参数部分。
- getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
- getRemoteAddr方法返回发出请求的客户机的IP地址
- getRemoteHost方法返回发出请求的客户机的完整主机名
- getRemotePort方法返回客户机所使用的网络端口号
- getLocalAddr方法返回WEB服务器的IP地址。
- getLocalName方法返回WEB服务器的主机名
获得客户机请求头
- getHeader(string name)方法:String
- getHeaders(String name)方法:Enumeration
- getHeaderNames()方法
获得客户机请求参数(客户端提交的数据)
- getParameter(String)方法**(常用)**
- getParameterValues(String name)方法**(常用)**
- getParameterNames()方法(不常用)
- getParameterMap()方法**(编写框架时常用)**
具体用法:javaweb学习总结(十)——HttpServletRequest对象(一) - 孤傲苍狼 - 博客园 (cnblogs.com)
常见应用
1、中文参数乱码问题
产生乱码,就是因为服务器和客户端沟通的编码不一致造成的,因此解决的办法是:在客户端和服务器之间设置一个统一的编码,之后就按照此编码进行数据的传输和接收。
由于客户端是以UTF-8字符编码将表单数据传输到服务器端的,因此服务器也需要设置以UTF-8字符编码进行接收,要想完成此操作,服务器可以直接使用从ServletRequest接口继承而来的”setCharacterEncoding(charset)”方法进行统一的编码设置。修改后的代码如下:
1 2 3 4 5 6 7 8 9
| public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); String userName = request.getParameter("userName"); System.out.println("userName:"+userName); }
|
以get方式提交表单中文参数乱码问题
单纯使用 request.setCharacterEncoding(“UTF-8”); 无法解决问题。
解决办法:在接收到数据后,先获取request对象以ISO8859-1字符编码接收到的原始数据的字节数组,然后通过字节数组以指定的编码构建字符串,解决乱码问题。
1 2 3 4 5 6 7 8 9 10
| public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name"); name =new String(name.getBytes("ISO8859-1"), "UTF-8") ; System.out.println("name:"+name); }
|
2、防盗链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| String referer = request.getHeader("Referer");
if ( referer == null || !referer.contains("localhost:8080/zhongfucheng/index.jsp") ) {
response.sendRedirect("/zhongfucheng/index.jsp"); return; }
response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("路飞做了XXXXxxxxxxxxxxxxxxxx");
|
3、实现转发
请求转发:指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
请求转发的应用场景:MVC设计模式
endRedirect()可以实现重定向,做到的功能是页面跳转,使用request的getRequestDispatcher.forward(request,response)实现转发,做到的功能也是页面跳转。
1 2 3 4 5
| RequestDispatcher requestDispatcher = request.getRequestDispatcher("/index.jsp");
requestDispatcher.forward(request, response);
|
- ServletContext也能称之为域对象。而request也可以称之为域对象,只不过ServletContext的域是整个web应用,而request的域仅仅代表一次http请求
- 一般的原则:可以使用request就尽可能使用request。因为ServletContext代表着整个web应用,使用ServletContext会消耗大量的资源,而request对象会随着请求的结束而结束,资源会被回收。使用request域进行Servlet之间的通讯在开发中是非常频繁的
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class RequestDemo06 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String data="大家好";
request.setAttribute("data", data); request.getRequestDispatcher("/test.jsp").forward(request, response); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
test.jsp页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Request对象实现请求转发</title> </head>
<body> 使用普通方式取出存储在request对象中的数据: <h3 style="color:red;"><%=(String)request.getAttribute("data")%></h3> 使用EL表达式取出存储在request对象中的数据: <h3 style="color:red;">${data}</h3> </body> </html>
|
转发和重定向的区别
实际发生位置不同,地址栏不同
- 转发是发生在服务器的
- 转发是由服务器进行跳转的,细心的朋友会发现,在转发的时候,浏览器的地址栏是没有发生变化的。也就是说浏览器是不知道该跳转的动作,转发是对浏览器透明的。通过上面的转发时序图我们也可以发现,实现转发只是一次的http请求,一次转发中request和response对象都是同一个。这也解释了,为什么可以使用request作为域对象进行Servlet之间的通讯。
- 重定向是发生在浏览器的
- 重定向是由浏览器进行跳转的,进行重定向跳转的时候,浏览器的地址会发生变化的。曾经介绍过:实现重定向的原理是由response的状态码和Location头组合而实现的。这是由浏览器进行的页面跳转实现重定向会发出两个http请求,request域对象是无效的,因为它不是同一个request对象
用法不同
很多人都搞不清楚转发和重定向的时候,资源地址究竟怎么写。有的时候要把应用名写上,有的时候不用把应用名写上。很容易把人搞晕。记住一个原则:给服务器用的直接从资源名开始写,给浏览器用的要把应用名写上
- request.getRequestDispatcher(“/资源名 URI”).forward(request,response)
- response.send(“/web应用/资源名 URI”);
能够去往的URL的范围不一样
- 转发是服务器跳转只能去往当前web应用的资源
- 重定向是浏览器跳转,可以去往任何的资源
传递数据的类型不同
- 转发的request对象可以传递各种类型的数据,包括对象
- 重定向只能传递字符串
跳转的时间不同
- 转发时:执行到跳转语句时就会立刻跳转
- 重定向:整个页面执行完之后才执行跳转
转发和重定向使用哪一个?
根据上面说明了转发和重定向的区别也可以很容易概括出来。转发是带着转发前的请求的参数的。重定向是新的请求。
典型的应用场景:
- 转发: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器里 URL 不变
- 重定向: 提交表单,处理成功后 redirect 到另一个 jsp,防止表单重复提交,浏览器里 URL 变了