1.基本概念
1.1 web开发
- web是网页的意思,表示我们可以从网站上取得一定的资源
- 静态web:
- html、css
- 提供给所有人看的数据,数据始终不会发生变化
- 动态web:
- 几乎所有的网站都是动态的
- 提供给所有人看的数据,数据始终会发生变化。每个人在不同的时间、地点看到的信息各不相同
- 常用技术栈:Serblet/JSP, ASP, PHP
- 在java中动态web资源开发的技术统称为javaWeb
1.2 web应用程序
- web应用程序:可以提供浏览器访问的程序
- 如xxx.html……多个web资源整合起来,这些资源可以被外界访问,对外界提供服务。
- 能够被访问到的任何一个资源或者页面,都存在于这个世界的某一台计算机上
- URL
- 这些统一的web资源会被放在同一个文件夹下,这就是一个web应用程序,依赖于Tomcat(服务器)去访问
- 一个web应用由多部分组成(静态web、动态web)
- html、css、js
- jsp、servlet
- java程序
- jar包
- 配置文件(properties)
- web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理
1.3 静态web
- *.htm、 *.html是网页的后缀,若服务器上一直存在这些东西,就可以直接通过网络进行读取。
- 流程如下:
- 静态web的缺点:
- web页面无法更新,所有用户看到的页面相同
- 轮播图,点击特效:伪动态(使用javaScript实现)
- 无法和数据库交互,数据无法持久化,用户无法交互
- web页面无法更新,所有用户看到的页面相同
1.4 动态web
- 页面可以动态展示,每个用户看到的页面都不一样。web页面展示的效果因人而异。
- 流程如下
- 缺点:若服务器的动态web资源出错,就需要重新编写后台程序并且重新发布,代价会有点大
- 优点:
- web页面可以动态更新,每个用户看到的页面都不一样
- 可以和数据库交互,可以做数据持久化,存储用户数据
2.web服务器
2.1 技术
ASP:
- 国内最早流行的,来自微软
- 在html中嵌入了VB脚本
- 在ASP开发中,一个页面往往会有几千行的业务代码,极难维护
PHP:
- 开发速度快、功能强大、跨平台、代码简单
- 无法承载大量访问的情况,具有局限性,仅适用于中小型的网站
JSP:
- 基于java语言
- 可以承载高并发、高可用、高性能带来的影响
- 语法和ASP相似,加强市场竞争力
2.2 web服务器
- 服务器是一种被动的操作,用来处理用户的请求,并给用户响应
- IIS
- Tomcat
3. Tomcat详解
3.1 安装tomcat
- tomcat官网下载最新的安装包
- 解压
3.2 Tomcat启动
- Tomcat文件夹目录介绍
- bin目录下是启动、关闭的脚本文件,存放tomcat命令
- conf是配置文件,里面的server.xml文件是核心的配置文件
- lib是依赖的jar包
- logs是日志
- webapps存放网站
- mac操作系统启动、关闭Tomcat的方式
- 将下载好的tomcat压缩包移入/usr/local目录下
sudo mv /Users/taozehua/Downloads/apache-tomcat-10.0.10.tar.gz /usr/local/ - 解压缩在/usr/local/目录
sudo (需要先进入特权模式)
cd /usr/local/
tar zxvf apache-tomcat-10.0.10.tar.gz - 建立链接,方便维护使用
sudo
ln -s /usr/local/apache-tomcat-10.0.10/Library/Tomcat - 命令行启动Tomcat:
/Library/Tomcat/bin/startup.sh
若出现如下提示则表示安装并运行成功:Using CATALINA_BASE: /Library/Tomcat Using CATALINA_HOME: /Library/Tomcat Using CATALINA_TMPDIR: /Library/Tomcat/temp Using JRE_HOME: /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
- 打开浏览器,输入 http://localhost:8080/
回车之后如果看到Apache Tomcat,表示已经成功运行Tomcat - 停止tomcat:
/Library/Tomcat/bin/shutdown.sh
- 将下载好的tomcat压缩包移入/usr/local目录下
3.3 Tomcat配置
- Tomcat配置,主要的配置都可以在conf目录下的server.xml中修改
- 可以配置启动的端口号,tomcat的默认端口号为8080
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
- 可以配置主机的名称(如把localhost换成www.baidu.com)
- 默认主机名为localhost,等价于127.0.0.1
- 默认网站应用存放的位置为webapps目录
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
- 请你谈谈网站是如何进行访问的?(重要面试题)
- 输入一个域名、回车
- 检查本机的host配置文件下有没有这个域名的映射。
- 有就直接返回对应的ip地址(如本机上localhost对应于ip地址127.0.0.1),这个地址中有我们需要访问的web程序,可以直接访问
- 没有就去DNS服务器(管理域名和ip地址的映射)上找,找的话就返回,找不到就报错。
- 配置环境变量(方便启动、可以不配置)
3.4 发布一个web网站的流程
- 将自己写的网站放到服务器(tomcat)中指定的web应用的文件夹下,这里是webapps目录下。如这里的tao文件夹下面就是自己写的网页。
文件夹结构如下
这时启动tomcat服务器,并且使用浏览器访问http://localhost:8080/tao/就会显示出自己写的网站 - 网站应有的结构:
--webapps: Tomcat服务器的web目录 --ROOT --tao:自己写的网站的目录名 -WEB-INF -classes:java程序 -lib:web应用所依赖的jar包 -web.xml:网站的配置文件 -index.html:默认的首页 - static -css -js -img -……
4.Http详解
4.1 什么是Http
- Http(超文本传输协议)是一个简单的请求响应协议,通常运行在TCP之上。
- 超文本:图片、音乐、视频、地图……
- http默认端口:80
- Https:(安全的)
- 默认端口:443
4.2 两个时代
- http1.0
- HTTP/1.0:客户端与web服务器连接后,只能获得一个web资源,断开连接(再要使用的时候需要重新连接请求)
- http2.0
- HTTP/1.1:客户端与web服务器连接后,只能获得多个web资源
4.3 Http请求(Request)
- 客户端向服务器发送请求
- 以百度为例:
- Request URL:请求地址
- Request Method:请求方法
- Status Code:状态码
- Remote Address:远程地址+端口
Request URL: https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=&hisdata=&_t=1631334447290&req=2&csor=0 Request Method: GET Status Code: 200 OK Remote Address: 127.0.0.1:7890
Accept: application/json, text/javascript Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive Cache-Control: max-age=0
- 请求行:
- 请求行中的请求方式:GET、POST、HEAD、DELETE、PUT……
- get:一次请求能够携带的参数比较少,大小有限制,会在浏览器的url地址栏显示数据内容,不安全但高效。
- post:一次请求能够携带的参数没有限制,大小没有限制,不会在浏览器的url地址栏显示数据内容,安全但不高效。
- 请求行中的请求方式:GET、POST、HEAD、DELETE、PUT……
- 消息头:
- Accept:告诉浏览器,它所支持的数据类型
- Accept-Encoding:支持哪种编码格式
- Accept-Language:告诉浏览器,它的语言环境
- Cache-Control:缓存控制
- Connection: 告诉浏览器请求完成是断开还是保持连接
- HOST:主机
4.4 Http响应(Response)
- 服务器响应给客户端
- 以百度为例:
- Cache-Control:缓存控制
- Connection:keep-alive 保持连接
- Content-Encoding:编码
- Content-Type:类型
Cache-Control: max-age=315360000 Connection: keep-alive Content-Encoding: gzip Content-Type: application/javascript
- 响应体:
- Accept:告诉浏览器,它所支持的数据类型
- Accept-Encoding:支持哪种编码格式
- Accept-Language:告诉浏览器,它的语言环境
- Cache-Control:缓存控制
- Connection: 告诉浏览器请求完成是断开还是保持连接
- HOST:主机
- Refresh:告诉客户端,多久刷新一次
- Location:让网页重新定位
- 响应状态码:
- 200:请求响应成功
- 3xx:请求重定向(重新定位到我给你的新位置去)
- 4xx:找不到资源,资源不存在(404)
- 5xx:服务器代码错误(502:网关错误、500……)
- **常见面试题:**当浏览器中地址栏输入地址并回车的一瞬间到页面能够展示,经历了什么?
5. Maven
在javaweb开发中,需要使用大量的jar包,手动导入太麻烦,需要Maven来自动导入和配置jar包。
5.1 Maven(项目架构管理工具)
- 使用Maven来自动导入和配置jar包
- Maven的核心思想:约定大于配置(有约束不要去违反)
- Maven会规定好该如何去编写java代码,必须要按照规范来(如代码的目录结构的规定)
5.2 在IDEA中使用Maven
maven项目创建步骤:
- 选择webapp模版进行创建maven项目
- 等待一段时间后导入成功,并且自己加入java目录和resources目录(可能会需要自己标记目录类别),最后完整的项目结构如下:
5.3 在IDEA中配置Tomcat
- 点击右上角Add Configuration……
- 点击左上角的加号添加配置,并且找到Tomcat Server,选择本地
- 设置Tomcat的服务器启动名称、JDK环境和Tomcat的默认启动端口号。
注意还需要在Application Server一栏中选择自己电脑上的tomcat所在的路径(如果出现Application server libraries not found可能是因为tomcat版本过高不兼容,降低tomcat版本就好)
若出现其他问题点击fix即可 - 配置完成后右上角就可以启动tomcat了
- 启动成功
- 使用浏览器访问http://localhost:8080/javawebmaven_war/就可以得到Hello World!这里访问到的Hello World!就是项目目录webapp下index.jsp里面的内容
6. Servlet
6.1 Servlet简介
- Servlet是sun公司开发动态web的一门技术
- sun公司在这些API中提供一个接口,叫做Servlet。开发一个Servlet程序只需要完成:
- 编写一个类,实现Servlet接口
- 把开发好的java类部署到web服务器中
- 把实现了Servlet接口的java程序叫做Servlet,Servlet接口Sun公司有两个默认的实现类:HttpServlet、GenericServlet
6.2 HelloServlet
- 构建一个普通的maven项目,不需要以webapp为模版。
- 在pom.xml中导入Servlet和JSP的依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency>
- 在项目下新建一个Model:Servlet-01,相当于一个子模块
父项目中会有
父项目中的jar包子项目可以直接使用,子项目的jar包父项目不可以直接使用<modules> <module>Servlet-01</module> </modules>
- Maven环境优化:
- 修改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"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
- 将maven的结构搭建完整,补上java目录和resources目录
- 修改web.xml为最新的
- 编写一个Servlet程序
- 编写一个普通类
- 实现Servlet接口,直接继承HttpServlet
public class HelloServlet extends HttpServlet { // 由于get和post只是请求实现的不同方式,可以相互调用,业务逻辑都一样 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //ServletInputStream inputStream = req.getInputStream(); //BufferedReader reader = req.getReader(); //ServletOutputStream outputStream = resp.getOutputStream(); PrintWriter writer = resp.getWriter(); // 响应流 writer.println("Hello Servlet!"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- 编写Servlet的映射
- 为什么需要映射?我们写的是java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以需要在web服务中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径
- 在web.xml中进行Servlet的注册和映射
<web-app> <!--注册Servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.example.servlet.HelloServlet</servlet-class> </servlet> <!--Servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
- 配置Tomcat:见5.3节
- 启动测试,使用浏览器访问http://localhost:8080/Servlet01_war/hello就会显示出打印出来的Hello Servlet!
6.3 Servlet原理
Servlet是由web服务器调用,web服务器在收到浏览器请求之后
6.4 Mapping
- 一个Servlet可以指定一个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
- 一个Servlet可以指定多个映射路径,这时候访问/hello、/hello1……都可以得到对应的页面
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping>
- 一个Servlet可以指定通用映射路径,映射为/hello/*后访问/hello+任意内容(或者不加)都可以访问到该页面
<!--Servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
- 如果直接写/*(默认请求路径),那么默认页面就不再是index.jsp对应的那个页面,而是hello对应的页面
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
- 可以自己指定一些后缀,这时候*前面不能加任何映射的路径,即不能出现“/”。(这时候访问/hello/sadhha.truth也能访问到hello)
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.truth</url-pattern> </servlet-mapping>
- 优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
6.5 ServletContext对象的应用
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用,可以对当前的web应用进行管理
6.5.1 共享数据:在一个Servlet中保存的数据可以在另一个Servlet中拿到
- 先实现一个放置数据的servlet类。该类中先使用getServletContext方法得到ServletContext对象,再由setAttribute方法将数据保存在ServletContext中
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext();// servlet上下文 String username = "tao"; // 数据 context.setAttribute("username",username); // 将一个数据保存在ServletContext中,名字为username,值为username } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- 再去实现一个servlet类获取数据。同样可以使用getServletContext方法得到ServletContext对象,并且通过getAttribute方法可以拿到HelloServlet中保存的数据。(注意:HelloServlet和GetServlet共享一个ServletContext对象,因此它们之间可以进行数据交换)
public class GetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 与HelloServlet共有一个ServletContext对象 ServletContext servletContext = this.getServletContext(); String username =(String) servletContext.getAttribute("username"); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); resp.getWriter().println("name:"+username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- 同样需要在web.xml中进行注册和映射
<servlet> <servlet-name>hello</servlet-name> <servlet-class>com.example.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>get</servlet-name> <servlet-class>com.example.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>get</servlet-name> <url-pattern>/get</url-pattern> </servlet-mapping>
- 最后运行项目,先访问http://localhost:8080/Servlet02_war/hello,再去访问http://localhost:8080/Servlet02_war/get就可以得到在HelloServlet中设置的用户名。注意访问顺序不能颠倒,因为需要先设置再读取。
6.5.2 获取初始化参数
- 在web.xml中配置web应用的初始化参数
相当于一个键值对,key是url,value是jdbc:mysql://localhost:3306/mybatis<context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param>
- 实现一个DemoServlet类来获取初始化参数。通过servletContext.getInitParameter(“url”)方法就可以获得url对应的参数
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); System.out.println(servletContext.getInitParameter("url")); }
- 当然仍然需要在web.xml中进行注册和映射
<servlet> <servlet-name>gp</servlet-name> <servlet-class>com.example.servlet.DemoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>gp</servlet-name> <url-pattern>/gp</url-pattern> </servlet-mapping>
- 运行项目后访问http://localhost:8080/Servlet02_war/gp就会在控制台输出jdbc:mysql://localhost:3306/mybatis
- 注意如果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"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
6.5.3 请求转发
- 实现一个DemoServlet2类进行请求转发。这里使用getRequestDispatcher和forward方法将请求转发到6.5.2节实现的DemoServlet类对应的页面
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp"); // 设置转发的请求路径 requestDispatcher.forward(req,resp); // 调用forward方法实现请求转发 }
- 在web.xml中进行注册
<servlet> <servlet-name>dp</servlet-name> <servlet-class>com.example.servlet.DemoServlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>dp</servlet-name> <url-pattern>/dp</url-pattern> </servlet-mapping>
- 此时运行项目并且使用浏览器访问http://localhost:8080/Servlet02_war/dp就会显示出/gp页面的内容。可以理解为自动跳转到了该页面,但url路径没变,仍然是/dp。
- 通常应用于以下场景:
- a需要向c请求资源,但a只能访问b,这时候就需要b进行请求转发(a始终只与b直接交流)。
- 不同于重定向,重定向通常是a向b请求资源,然后b告诉a资源在c上,a再去向c请求资源。这时a和b、c都有交流
6.5.4 读取资源文件
要使用Properties类
- 在java目录下新建properties
- 在resources目录下新建properties
项目运行后这两个properties都被打包到了一个路径下:classes,我们称这个路径为classpath
- 在resources目录下新建一个properties文件,填入如下内容
username=root password=123456
- 实现一个PropertiesServlet类来读取资源。使用ServletContext对象的getResourceAsStream方法(方法参数需要填入资源所在位置的相对路径)获得一个文件流,然后通过Properties类进行加载就可以得到properties文件中存储的内容。
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); resp.getWriter().println(properties.getProperty("username")); resp.getWriter().println(properties.getProperty("password")); }
- 在web.xml中进行注册和映射
<servlet> <servlet-name>prop</servlet-name> <servlet-class>com.example.servlet.PropertiesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>prop</servlet-name> <url-pattern>/prop</url-pattern> </servlet-mapping>
- 运行项目后使用浏览器访问http://localhost:8080/Servlet02_war/prop就会得到
root 123456
6.6 HttpServletResponse
- web服务器接收到客户端的http请求,会针对这个请求分别创建一个代表请求的HttpServletRequest对象和代表相应的HttpServletResponse对象。
- 若要获取客户端请求来的参数,就要使用HttpServletRequest
- 若要给客户端响应一些信息,就要使用HttpServletResponse
6.6.1 方法分类
负责向浏览器发送数据的方法
getOutputStream方法负责向浏览器写流,getWriter方法负责向浏览器写中文
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
响应的状态码
public static final int SC_CONTINUE = 100;
public static final int SC_SWITCHING_PROTOCOLS = 101;
public static final int SC_OK = 200;
public static final int SC_CREATED = 201;
public static final int SC_ACCEPTED = 202;
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
public static final int SC_NO_CONTENT = 204;
public static final int SC_RESET_CONTENT = 205;
public static final int SC_PARTIAL_CONTENT = 206;
public static final int SC_MULTIPLE_CHOICES = 300;
public static final int SC_MOVED_PERMANENTLY = 301;
public static final int SC_MOVED_TEMPORARILY = 302;
public static final int SC_FOUND = 302;
public static final int SC_SEE_OTHER = 303;
public static final int SC_NOT_MODIFIED = 304;
public static final int SC_USE_PROXY = 305;
public static final int SC_TEMPORARY_REDIRECT = 307;
public static final int SC_BAD_REQUEST = 400;
public static final int SC_UNAUTHORIZED = 401;
public static final int SC_PAYMENT_REQUIRED = 402;
public static final int SC_FORBIDDEN = 403;
public static final int SC_NOT_FOUND = 404;
public static final int SC_METHOD_NOT_ALLOWED = 405;
public static final int SC_NOT_ACCEPTABLE = 406;
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
public static final int SC_REQUEST_TIMEOUT = 408;
public static final int SC_CONFLICT = 409;
public static final int SC_GONE = 410;
public static final int SC_LENGTH_REQUIRED = 411;
public static final int SC_PRECONDITION_FAILED = 412;
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
public static final int SC_REQUEST_URI_TOO_LONG = 414;
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
public static final int SC_EXPECTATION_FAILED = 417;
public static final int SC_INTERNAL_SERVER_ERROR = 500;
public static final int SC_NOT_IMPLEMENTED = 501;
public static final int SC_BAD_GATEWAY = 502;
public static final int SC_SERVICE_UNAVAILABLE = 503;
public static final int SC_GATEWAY_TIMEOUT = 504;
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
}
6.6.2 常见应用
- 向浏览器输出数据、信息
ServletOutputStream getOutputStream() throws IOException; PrintWriter getWriter() throws IOException;
- 下载文件
- 获取下载文件的路径。之所以使用
"/WEB-INF/classes/t.jpg"
来获得文件的绝对路径,是因为项目运行后在target目录下的WEB-INF/classes中会生成图片文件。String realPath =this.getServletContext().getRealPath("/WEB-INF/classes/t.jpg"); System.out.println(realPath);
- 获取下载的文件名,路径下最后一个"/" 后面的一定是文件名
String filename = realPath.substring(realPath.lastIndexOf("//") + 1);
- 设置让浏览器能够支持下载我们需要的东西。设置响应头Content-Disposition,表示文件在浏览器中下载。使用URLEncoder.encode可以让中文文件名用UTF-8进行编码,解决在浏览器中的乱码问题。
resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename,"UTF-8"));
- 获取下载文件的输入流
FileInputStream fileInputStream = new FileInputStream(realPath);
- 创建缓冲区,通常使用一个1024长度的字节数组来充当缓冲区
int len=0; byte[] buffer = new byte[1024];
- 获得OutputStream对象
ServletOutputStream outputStream = resp.getOutputStream();
- 将FileOutputStream写入到缓冲区(buffer),并且使用OutputStream将缓冲区中的数据输出到客户端
while((len=fileInputStream.read(buffer))>0){ // 使用OutputStream将缓冲区中的数据输出到客户端 outputStream.write(buffer,0,len); }
- 关闭流
fileInputStream.close(); outputStream.close();
- 获取下载文件的路径。之所以使用
- 验证码功能
- 验证码怎么来的?
- 前端实现需要通过js来判断
- 后端实现,需要用到java的图片类,生成一个图片
- 首先实现一个随机数生成器
// 生成随机数 private String makeNum(){ Random random = new Random(); // 4个9代表生成的是小于等于四位数 String num= String.valueOf(random.nextInt(9999)); StringBuffer stringBuffer = new StringBuffer(); // 用0填充 for(int i=0;i<4-num.length();i++){ stringBuffer.append("0"); } return stringBuffer.toString() + num; }
- 在内存中生成一个图片并向图片写入随机数的数据
// 在内存中创建一个图片 BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); // 得到图片 Graphics2D graphics = (Graphics2D)image.getGraphics(); // 相当于画笔 // 设置图片的背景颜色 graphics.setColor(Color.white); graphics.fillRect(0,0,80,20); // 给图片写数据 graphics.setColor(Color.blue); graphics.setFont(new Font(null,Font.BOLD,20)); graphics.drawString(makeNum(),0,20);
- 设置响应头,使得浏览器不缓存图片,最后将图片写到浏览器
// 让浏览器3秒自动刷新一次 resp.setHeader("refresh","3"); // 3秒刷新一次 // 告诉浏览器这个请求用图片的方式打开 resp.setContentType("image/jpeg"); // 不让浏览器缓存 resp.setDateHeader("expires",-1); // expires设置过期时间 resp.setHeader("Cache-Control","no-cache"); // 不缓存,不会占用浏览器资源 resp.setHeader("Pragma","no-cache"); // 把图片写给浏览器 ImageIO.write(image, "jpg",resp.getOutputStream());
- 验证码怎么来的?
- 实现重定向
- 重定向:一个web资源受到客户端请求后,他会通知客户端去访问另一个web资源。
- 常见场景:用户登录
- 使用sendRedirect来实现重定向
void sendRedirect(String location) throws IOException;
- 我们以跳转至/image页面为例子,跳转的路径前面要加上项目名称“/HttpServletResponse_war”,否则会跳转失败
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("/HttpServletResponse_war/image"); }
- 注册后再去访问http://localhost:8080/HttpServletResponse_war/redirect就会自动跳转到http://localhost:8080/HttpServletResponse_war/image页面。这时候url路径发生改变,与请求转发不同
- 上述的sendRedirect方法可以使用如下代码代替,效果一样
resp.setHeader("Location","/HttpServletResponse_war/image"); // 状态码设置为重定向 resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
- 面试题:重定向和转发的区别
- 相同点:页面都会跳转
- 不同点:请求转发时url不会发生变化;重定向时url地址栏会发生变化
6.7 HttpServletRequest
- HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器
- Http请求中的所有信息会被封装到HttpServletRequest,通过HttpServletRequest的方法,可以获得客户端的所有信息
6.7.1 获取前端传递的参数以及请求转发
- 首先需要引入jsp的依赖
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency>
- 用jsp实现登录页面和登录成功页面,模拟前端
- 登录页面,action="${pageContext.request.contextPath}/login"和method="post"代表以post方式提交表单,提交到web项目下的/login请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Login</title> </head> <body> <h1>登录</h1> <div style="text-align: center"> <%--以post方式提交表单,提交到login请求--%> <form action="${pageContext.request.contextPath}/login" method="post"> 用户名:<input type="text" name="username"> <br> 密码:<input type="password" name="password"> <br> 爱好: <input type="checkbox" name="hobby" value="打篮球">打篮球 <input type="checkbox" name="hobby"value="唱歌">唱歌 <input type="checkbox" name="hobby" value="电影">电影 <br> <input type="submit"> </form> </div> </body> </html>
- 登录成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录成功</title> </head> <body> <h1>登录成功</h1> </body> </html>
- 登录页面,action="${pageContext.request.contextPath}/login"和method="post"代表以post方式提交表单,提交到web项目下的/login请求
- 实现一个LoginServlet类使用req.getParameter和req.getParameterValues方法来获得前端传来的参数,并使用req.getRequestDispatcher方法进行请求转发。
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 解决乱码问题 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); // 获得前端传来的参数 String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbies = req.getParameterValues("hobby"); System.out.println("---------------------------"); System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobbies)); // 通过请求转发 // 请求转发的路径中的"/"代表当前web项目,这里跳转到success.jsp对应的页面下面 req.getRequestDispatcher("/success.jsp").forward(req,resp); }
- 在web.xml中进行注册和映射
<servlet> <servlet-name>login</servlet-name> <servlet-class>com.example.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
- 运行项目后访问http://localhost:8080/HttpServletRequest_war/就会进入登录页面,填入参数后点击提交按钮就会跳转到登录成功页面,且在控制台会输出用户名、密码和爱好。至此已经成功的从前端获得了参数并且进行了请求转发。