前言
Web框架就是别人做好的1个半成品,别人干一部分活,我们干一部分活,完成1个eb应用程序的开发。
作为基于Web框架开发的开发人员,我们把学习的重点放到
- 如何从Web框架的request 请求对象中获取数据;
- 执行自己的业务逻辑
- 通过response响应对象中保存业务逻辑处理结果响应给浏览器;
一、HTTP协议简介
HTPP协议的作用:规范浏览器和服务器之间信息交互的标准。
1.什么是超文本?
文本:HTML、字符串
超文本:图片、音乐、视频、定位、地图等。
2.HTTP2.0时代说的是什么
HTTP分为2个时代,目前我们使用http1.1版本,也就是HTTP协议的第2个版本,所以我们称当前是HTTP2.0时代。
http1.0时代
HTTP/1.0版本:客户端和服务端建立TCP连接之后,1次只能获取1份web资源。
http2.0时代
HTTP/1.1版本:客户端和服务器建立TCP连接之后,1次可以获取多份web资源。
3.HTTP请求和响应
HTTP协议只能传输字符串文本数据。
注意:图片、视频、文件等二进制数据都要经过BASE64或MIME等编码为字符串形式,才能通过HTTP协议进行传输。
HTTP的请求、HTTP响应,传输的都是键值对字符串。
3.1.HTTP请求信息结构
一次HTTP请求包括:请求行、 请求头、请求体信息。
3.2.HTTP响应信息结构
一次HTTP响应包括:响应行、响应头、响应体信息。
二、request、response对象
由于HTTP协议传输的是键值对字符串,Web框架一般会通过2个对象,方便Web框架的使用者,封装HTTP请求和响应信息键值对字符串。
Tomcat处理一次HTTP请求的流程如下:
1.Tomcat接收到浏览器发送的HTTP请求,会创建Java对象,用于封装保存请求行、请求头、请求体字符串信息, 以下我称之为request对象。
2.Tomcat还会创建1个对象,用于封装保存响应行、响应头、响应体字符串信息,以响应给浏览器, 以下我称之为response对象。
3.Tomcat根据request请求携带的URL路径,锁定该URL对应的Servlet,调用该Servlet的service方法,并把以上2步创建的request对象和response对象当作参数传入到service方法中。
- 3.1.Web开发人员通过service方法中request对象参数获取请求的头、行、体。
- 3.2.Web开发人员在service方法中编写业务逻辑
- 3.3.Web开发人员将自己想要返回到浏览器的内容,设置到response对象中。
4.Tomcat将response对象解析成响应行、响应头、响应体字符串信息,响应给浏览器。
下面将学习如果通过Tomcat创建的request、response对象的API,达成以下目的
- 获取客户端请求信息
- 根据客户端请求信息,在后台执行不同的业务逻辑,得到执行结果。
- 把业务逻辑执行结果,响应给客户端浏览器
三、request对象
以下把Tomcat通过实现类(RequestFacade)创建出来用于封装请求信息的对象称为request对象。
Request对象代表客户端的请求,用户通过浏览器访问服务器时,HTTP请求中所有的信息都封装在这个对象中。
Request对象是由Tomcat创建,开发者可以直接使用这个对象提供的方法获取客户端的请求信息(行 头 体)。
ServletRequest 接口 | HttpServletRequest 接口 | org.apache.catalina.connector.RequestFacade 实现类由tomcat提供,多态:接口引用HttpServletReques指向实现类对象request
1.request对象获取请求消息
HTTP请求信息可分为请求行(请求的URL)、请求头和请求体信息。
1.1.获取请求行信息
package cn.zhanggen.web.servlet; import javax.servlet.GenericServlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/zhanggen") public class ServletDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取请求方式:GET或者POST System.out.println(req.getMethod()); //2.获取虚拟目录 System.out.println(req.getContextPath()); //3.获取Servlet 路径 System.out.println(req.getServletPath()); //4.获取请求参数字符串 System.out.println(req.getQueryString()); //5.获取URI(统一资源标识符):/zhanggen System.out.println(req.getRequestURI()); //6.获取URL(统一资源定位符):http://127.0.0.1:8080/zhanggen System.out.println(req.getRequestURL()); //7.获取协议版本 System.out.println(req.getProtocol()); //8.获取客户机的IP地址 System.out.println(req.getRemoteAddr()); System.out.println("doGet方法在执行"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
1.2.获取请求头信息
package cn.zhanggen.web.servlet; 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.util.Enumeration; @WebServlet("/index") public class ServletDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.获取所有请求头名称 Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); String value = request.getHeader(name); System.out.println(name + "<------------------->" + value); } //2.获取agent使用的浏览器信息:可用于浏览器兼容性 String agent = request.getHeader("user-agent"); System.out.println(agent); //3.获取refer信息可以进行防盗链操作:http://localhost:8080/login.html System.out.println(request.getHeader("referer")); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { } }
1.3.获取请求体信息
只有POST请求才会有请求体信息。
获取字符输入流:
BufferedReader bufferReader = request.getReader();
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { //1.获取字符流 BufferedReader bufferReader = request.getReader(); String line = null; while ((line = bufferReader.readLine()) != null) { System.out.println(line); } }
获取字节输入流:
ServletInputStream inputStream = request.getInputStream();
2.获取请求参数通用方式
以下方法可以获取GET和POST请求携带的参数和值
package cn.zhanggen.web.servlet; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.util.Arrays; import java.util.Enumeration; import java.util.Map; import java.util.Set; @WebServlet("/index") public class ServletDemo1 extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { //1.获取请求参数信息:根据名称获取参数值,GET和POST请求通用。 String username = request.getParameter("username"); String password = request.getParameter("password"); //2.获取多选框的值 String[] cars = request.getParameterValues("cars"); System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(cars)); //3.获取所有参数的名称 Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String s = parameterNames.nextElement(); System.out.println(s); } //4.获取所有参数和值的map集合 Map<String, String[]> parameterMap = request.getParameterMap(); Set<String> keyset = parameterMap.keySet(); for (String name : keyset) { String[] values = parameterMap.get(name); System.out.println(Arrays.toString(values)); } } }
3.获取请求参数乱码问题
package cn.zhanggen.web.servlet; 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; @WebServlet("/zhanggen66") public class ServletDemo3 extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { //1.设置字符流的编码 request.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); System.out.println(username); } }
4.request请求转发
一次请求在服务器内部资源之间跳转的方式,请求转发的特点:
- 1. 地址栏不发生改变
- 2. 只能转发到服务器内部资源
- 3. 转发是一次请求
package cn.zhanggen.web.servlet; 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; @WebServlet("/zhanggen66") public class ServletDemo3 extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); //1.转发请求 //request.getRequestDispatcher("/index").forward(request, resp); //2.响应字符串 response.setCharacterEncoding("utf-8"); response.setHeader("Content-Type", "text/html;charset=utf-8"); response.getWriter().write("登录成功"); } }
5.request对象保存数据
我们可以使用request对象,在1次请求中保存数据。
- 1. 何时创建:Tomcat接收到一个请求,在调用service方法之前创建request对象
- 2. 何时销毁:当一个请求离开服务器(响应回到浏览器)的时候,Tomcat就会销毁这个对象
- 3. 作用范围:一次请求中
request.setAttribute("msg","222"); //设置数据 Object msg = request.getAttribute("msg"); //获取数据 msg=(String)msg; System.out.println(msg); request.removeAttribute("msg"); //删除数据
四、ServletContext对象
ServletContext对象由Web容器(Tomcat)创建,凌驾于所有Servlet之上。
ServletContext对象应用广泛,可以在Servlet之间传输数据、获取初始化参数、转发http请求、读取配置文件。
ServletContext servletContext = this.getServletContext();
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--1.注册servlet--> <servlet> <servlet-name>Set</servlet-name> <servlet-class>com.zhanggen.content.SetData</servlet-class> </servlet> <!--2.添加URL映射--> <servlet-mapping> <servlet-name>Set</servlet-name> <url-pattern>/set</url-pattern> </servlet-mapping> <!--1.注册servlet--> <servlet> <servlet-name>Get</servlet-name> <servlet-class>com.zhanggen.content.GetData</servlet-class> </servlet> <!--2.添加URL映射--> <servlet-mapping> <servlet-name>Get</servlet-name> <url-pattern>/get</url-pattern> </servlet-mapping> </web-app>
1.设置值
我们可以在servletContext对象中设置数据。
package com.zhanggen.content; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; public class SetData extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); HashMap myMap = new HashMap<String, String>(); myMap.put("name", "Martin"); myMap.put("age", "18"); servletContext.setAttribute("student", myMap); resp.sendRedirect("/s1/get"); } }
2.获取值
然后通过servletContext对象获取设置的数据。
package com.zhanggen.content; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; public class GetData extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); HashMap student = (HashMap) servletContext.getAttribute("student"); System.out.println(student); resp.setCharacterEncoding("utf-8"); PrintWriter writer = resp.getWriter(); writer.println(student); } }
3.获取初始化参数
我们可以通过servletContext对象,获取web.xml中配置的初始化参数。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置web应用初始化参数--> <context-param> <param-name>jdbc</param-name> <param-value>jdbc:mysql://192.168.0.98:3306/mybatis</param-value> </context-param> <!--1.注册servlet--> <servlet> <servlet-name>Set</servlet-name> <servlet-class>com.zhanggen.content.SetData</servlet-class> </servlet> <!--2.添加URL映射--> <servlet-mapping> <servlet-name>Set</servlet-name> <url-pattern>/set</url-pattern> </servlet-mapping> <!--1.注册servlet--> <servlet> <servlet-name>Get</servlet-name> <servlet-class>com.zhanggen.content.GetData</servlet-class> </servlet> <!--2.添加URL映射--> <servlet-mapping> <servlet-name>Get</servlet-name> <url-pattern>/get</url-pattern> </servlet-mapping> </web-app>
获取初始化参数
public class GetData extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); //获取web.xml中配置的Web应用的初始化参数 String jdbc = servletContext.getInitParameter("jdbc"); System.out.println(jdbc); resp.setCharacterEncoding("utf-8"); PrintWriter writer = resp.getWriter(); writer.println(jdbc);
3.请求转发(forward)
我们可以通过servletContext对象实现http请求转发。
在Servlet中request和servletContext对象都能实现http请求转发。
public class Demo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); //我们可以使用request和servletContext对象都能实现 请求转发 request.getRequestDispatcher("/get").forward(request, response); servletContext.getRequestDispatcher("/get").forward(request, response); }
4.读取配置文件
我们可以通过servletContext对象,读取当前项目resources目录下的配置文件。
我们可以在maven项目的pom.xml中配置当前项目中哪些目录下的配置文件可以在当前项目编译之后被导出。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>javaweb-servlet</artifactId> <groupId>com.zhanggen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>servlet-content</artifactId> <packaging>war</packaging> <name>servlet-content Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> <include>**/*.tld</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> <include>**/*.tld</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
通过ServletContext可以读取当前项目/resources目录下的配置文件。
public class PropertiesServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { //通过getServletContext读取当前目录的resources InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); String username = properties.getProperty("username"); String password = properties.getProperty("password"); System.out.println(username); System.out.println(password); response.getWriter().println(username + "" + password); }
五、response对象
以下把Tomcat通过实现类(RequestFacade)创建出来的封装响应信息的对象称为response对象。
Response对象封装了发往客户端的 响应数据、响应头,响应状态码等信息。
Response对象是由Tomcat服务器创建,开发者可以直接使用这个对象设置响应信息(行 头 体)。
ServletResponse 接口 | HttpServletResponse 接口 | org.apache.catalina.connector.ResponseFacade 实现类(由tomcat提供的),多态:接口引用 HttpServletResponse指向实现类对象 response
1.response下载文件
package com.zhanggen.servlet; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; public class FileServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.获取服务端下载文件的路径 String realPath = "D:\\javawebservlet\\response\\target\\classes\\小玲珑.png"; System.out.println(realPath); //2.下载文件名:最后1个/的下一个,斜杠需要转义// String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1); //3.设置浏览器能支持下载文件 response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); //4.获取下载文件的输入流 FileInputStream fileInputStream = new FileInputStream(realPath); //5.创建缓冲区 int len = 0; byte[] buffer = new byte[1024]; //6.创建输出流 ServletOutputStream outputStream = response.getOutputStream(); //7.将FileInputStream的流写入到buffer缓冲区,使用outputStream将缓冲区中的数据输出到客户端 while ((len = fileInputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } //8.关闭输入和输出流 fileInputStream.close(); outputStream.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
2.response生成验证码
package com.zhanggen.servlet; import sun.awt.image.BufferedImageDevice; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //如何让浏览器5秒刷新一次 response.setHeader("refresh", "3"); //在内存中创建图片 BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //得到图片 Graphics2D g = (Graphics2D) bufferedImage.getGraphics(); //设置图片的背景颜色 g.setColor(Color.white); g.fillRect(0, 0, 80, 20); //在图片上写数字 g.setColor(Color.BLUE); g.drawString(generateNumber(), 0, 20); //告诉浏览器,这个请求用图片的方法打开 response.setContentType("image/jpeg"); //网站存在缓存,设置不让浏览器缓存 response.setDateHeader("expires", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //把图片写给浏览器 ImageIO.write(bufferedImage, "jpg", response.getOutputStream()); } private String generateNumber() { Random random = new Random(); //生成0-99999999之间的数字 String number = random.nextInt(99999999) + ""; //确保在生成的数字少于7位的情况,把少去的几位补0 StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 7 - number.length(); i++) { stringBuffer.append("0"); } //生成的数字和补0的数字进行组装 number = stringBuffer.toString() + number; return number; } }
3.response重定向
请求重定向(redirect)和请求转发(forward)的区别
相同点:
- 都可以实现页面的跳转
不同点:
- 请求转发的时候URL地址栏不会发生变化
- 重定向发送2次http请求,转发发送1次
- 重定向的状态码为302,转发的状态码为307
package com.zhanggen.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String[] hobbies = request.getParameterValues("hobby"); System.out.println(Arrays.toString(hobbies)); System.out.println(username + "<--->" + password); //请求重定向 //response.sendRedirect("/s1/success.jsp"); //请求转发 request.getRequestDispatcher("/success.jsp").forward(request, response); } }