1.Servlet
1.概述
运行在服务器端的java小程序
图解:
- Servlet是一个接口,定义了Java类被Tomcat识别的规则。
- Servlet的开发就是实现Servlet接口,重写里面的方法。
2.快速入门
- 步骤
- 创建JavaEE项目
- 定义一个类,实现Servlet接口
- 实现接口中的抽象方法
- 配置Servlet
- 在刚才创建的web工程的src目录中创建包
org.wdzl.web.servlet
,并创建一个类ServletDemo
- 实现Servlet接口
注意:实现接口时会发现,找不到依赖
-
解决办法
- File —> Project Structure
-
选择 Library
-
选择 Tomcat 8.5.61—> Add Selected
1.service方法
//提供服务的方法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet");//写一条输出语句即可
}
2.配置Servlet
为什么要配置Servlet?
因为浏览器要访问动态资源是要通过URL,而URL 如何要和Servlet对应上,就是我们接下来要做的事情。
在web.xml
中配置
<!-- 配置Servlet -->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>org.wdzl.web.servlet.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<!-- 访问Servlet的虚拟路径 -->
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
配置好后通过localhost/demo1进行访问即可
3 servlet执行原理
执行原理
- 服务器接收到浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的
<url-pattern>
- 如果有,则会找到对应的
servlet-class
全类名 - tomcat将对应的字节码文件加载到内存中,并创建该类的对象
- 调用方法
4 Servlet的方法 和 生命周期
-
生命周期
- 被创建:执行init方法,只执行一次
- 提供服务: 执行service方法,执行多次
- 被销毁:执行destroy,只执行一次
-
方法
/**
* 初始化的方法
* 在Servlet被创建时执行,只会执行一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
/**
* 获取ServletConfig对象
* ServletConfig:Servlet的配置对象
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 提供服务方法
* 每次Servlet被访问时执行,执行多次。
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet");
}
/**
* 获取Servlet的一些信息,版本,作者等
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁的方法
* 在Servlet销毁或在服务器正常关闭时,被执行一次
*/
@Override
public void destroy() {
System.out.println("destroy...");
}
5 Servlet方法详解
-
init():
- 初始化的时机可以改变
- Servlet是单例模式,多个用户访问可能存在线程安全问题
- 解决:使用Servlet时,尽量不要在Servlet中使用成员变量 ,即使定义了成员变量,也不要有修改值的操作。
6 注解配置
配置文件方式配置比较麻烦,3.0之后的版本支持注解方式,比较方便
- 步骤:
- 创建JavaEE项目,选择3.0以上版本,可以不创建web.xml
- 定义一个类,实现Servlet接口
- 重写方法
- 在类上使用注解进行配置
- 准备工作:重新创建一个,这次不勾选
Create web.xml
- 定义一个类,实现Servlet接口,重写方法
- 在类上使用注解进行配置
- 注解 WebServlet
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定Servlet 的 name 属性,等价于 <servlet-name> 。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性。两个属性不能同时使用。使用时可以省略属性名。 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern> 标签。 |
loadOnStartup | int | 指定 Servlet 的加载顺序,等价于 <load-on-startup> 标签。 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数,等价于<init-param> 标签。 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式,等价于<async-supported> 标签。 |
description | String | 该 Servlet 的描述信息,等价于 <description> 标签。 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用,等价于 <display-name> 标签。 |
7 小常识:
- 工作空间项目 和 tomcat部署的web项目 并不是一个项目
tomcat部署位置:CATALINA_BASE\work
工作空间项目位置:项目右键
- WEB-INFO目录下的资源不能被浏览器直接访问
8 Servlet 继承体系结构
7.8 Servlet 继承体系结构
查询API 我们了解一下Servlet的继承体系结构:
Servlet 接口
|
GenericServlet 抽象类
|
HttpServlet 抽象类
-
GenericServlet 抽象类
- 它将Servlet接口中的其他方法都做了空实现,只留下了service()方法作为抽象方法
-
HttpServlet 抽象类
- 该抽象类是对Http协议的一种封装,可以简化操作。
9 HttpServlet
- 使用步骤
- 定义类,继承HttpServlet
- 重写doGet()/doPost()
- 查看源码,理解原理
HttpServlet 中的 service()方法
service()继续往下翻
因为Http有7种请求方式,我们只需要关注常用的两个 Get 和 Post
- get()方式效果演示
- 新建一个ServletDemo2
@WebServlet("/demo2")
public class ServletDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost...");
}
}
- 启动服务,直接用浏览器请求该Servlet
注意:浏览器默认访问方式是GET() 方式
- post() :方式
- post方式需要借助Html的``标签
<body>
<form action="demo2" method="post">
<input type="text" name="username">
<input type="submit" value="登录">
</form>
</body>
10 urlPatterns()
- 一个Servlet可以匹配多个访问路径
注解源码:
urlPatterns 的数据类型是String类型的数组,就表示 该servlet可以设置多个访问路径
演示案例:
@WebServlet(urlPatterns = {"/a","/a2","/a3"})
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo3 doGet()....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo3 doPost()....");
}
}
- 路径定义规则
- 路径匹配:
/xxx
- 目录结果匹配:
/xxx/xxx
- 扩展名匹配:
*.xxx
- 路径匹配:
案例:
//@WebServlet("/aa/bb") 通过/aa/bb 访问
//@WebServlet("/aa/*") 通过/aa/任意字符 访问
//@WebServlet("/*") 通过/任意字符访问
@WebServlet("*.do")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo4 doGet()");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo4 doPost()");
}
}
2. HTTP - 请求消息
2.1 概念
超文本传输协议:Hyper Text Transfer Protocol 超文本传输协议
2.2 特点
- 基于TCP/IP的高级协议:安全的
- 默认端口号:80
- 基于请求/相应模型:一次请求对应一次相应
图示:
- 无状态的:每次请求之间相互独立,不能交互数据
2.3 历史版本
演示:使用火狐浏览器
我们访问baidu首页一共发送了几次请求?实际访问百度主页会发送很多次请求,因为主页的每一个图片,文本内容都是一次请求
- 1.0:每一次请求响应都会建立新的连接
- 1.1:复用连接,请求连接不会立马释放,会保留一段时间,如果在时间内该链接还有数据进行传输,就会复用该连接。
3.请求消息- 数据格式
数据格式:
- 请求行
- 请求头
- 请求空行
- 请求体JSP + Servlet
演示案例:使用post()演示案例结合火狐浏览器查看数据格式
3.1 请求行
- 格式:请求方式 请求URL 请求协议/版本号
结合演示案例得出案例种请求行的信息:
GET /demo/login.html HTTP/1.1
-
**请求方式:**Http共有7种请求方式,常用的有2种
-
请求URL
- GET
- URL可能携带参数
- POST
- URL不携带参数
- GET
3.2 请求头
请求头内容就是浏览器告诉服务器自身的一些信息。
- 格式:请求头名称 : 请求头值 ,请求头值,……
结合演示案例得出案例种请求行的信息:
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
Origin: http://localhost:8080
Connection: keep-alive
Referer: http://localhost:8080/demo/login.html
Cookie: JSESSIONID=EA1651C97732902D299C5CD859AE1647; Idea-e44921c1=1d14f60c-0164-4cc4-9ed0-2d9d05535a13
Upgrade-Insecure-Requests: 1
常见的请求头
-
Host:发送请求的主机
-
User-Agent: 浏览器告诉服务器,当前浏览器的版本信息
- 可以在服务器端获取浏览器版本信息,来解决浏览器兼容问题(原因:静态页面的解析都是浏览器自己完成的。这就造成了相同的内容,在不同浏览器中可能出现不同的样式。)
-
Accept: 浏览器告诉服务器自己支持的数据格式
-
Referer:告诉服务器当前请求从哪里来
- 作用:
- 防盗链
- 统计工作
- 作用:
-
Connection:keep-alive (现在都使用的HTTP 1.1 所以该头信息被省略)
3.3 请求空行
- 空行:用于分割请求头 和请求体
3.4 请求体(正文)
封装POST 请求信息的请求参数
GET方式种没有请求体,我们可以看表单提交后的效果
- 参数 = 对应的值
- 请求消息-格式:
POST /demo/demo2 HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
Origin: http://localhost:8080
Connection: keep-alive
Referer: http://localhost:8080/demo/login.html
Cookie: JSESSIONID=EA1651C97732902D299C5CD859AE1647; Idea-e44921c1=1d14f60c-0164-4cc4-9ed0-2d9d05535a13
Upgrade-Insecure-Requests: 1
username=hahaha
4. Request 对象
4.1 Request 和 Response 对象的原理
- request 和 response对象是由服务器创建,我们使用即可
- request对象是用来获取请求信息,response对象用设置相应信息
4.2 Request继承体系
查询API
ServletRequest – 接口
| 继承
HttpServletRequest – 接口
| 实现(service中打印request对象)
org.apache.catalina.connector.RequestFacade – 在tomcat中实现
4.3 获取请求消息数据
4.3.1获取请求行数据
我们根据请求行数据格式分析需要的方法
GET /demo/demo2 HTTP/1.1
-
获取请求方式:GET
String getMethod()
-
获取虚拟目录(常用): /demo
String getContextPath()
-
获取Servlet路径: /demo2
String getServletPath()
-
获取get方式请求参数:name=zhangsan
String getQueryString()
-
获取请求URI(常用): /demo/demo2
String getRequestURI(): /demo/demo2
StringBuffer getRequestURL() :
http://localhost:8080/demo/demo2
-
获取协议及版本:HTTP/1.1
String getProtocol()
-
获取客户机的IP地址:
String getRemoteAddr()
演示案例:
@WebServlet("/req")
public class RequestDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求方式
String method = req.getMethod();
System.out.println(method);
// 获取虚拟目录
String contextPath = req.getContextPath();
System.out.println(contextPath);
// 获取Servlet路径
String servletPath = req.getServletPath();
System.out.println(servletPath);
// 获取get方式的请求参数
String queryString = req.getQueryString();
System.out.println(queryString);
// 获取URI
String requestURI = req.getRequestURI();
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURI);
System.out.println(requestURL);
// 获取协议版本
String protocol = req.getProtocol();
System.out.println(protocol);
//返回用户ip
String remoteAddr = req.getRemoteAddr();
System.out.println(remoteAddr);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
4.3.2 获取请求头数据
- String getHeader(String name):通过请求头的名称获取请求头的值(常用)
- Enumeration getHeaderNames():获取所有的请求头名称
* Enumeration 相当于迭代器,API中有说
- Enumeration getHeaderNames():获取所有的请求头名称
演示案例1:获取所有请求头数据
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取所有请求头名称
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
//获取请求头的键
String name = headerNames.nextElement();
//获取请求头的值
String value = req.getHeader(name);
System.out.println(name + "--" + value);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
演示案例2:获取用户浏览器信息
@WebServlet("/req3")
public class RequestDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取UserAgent
String agent = req.getHeader("user-agent");//请求头的key不区分大小写
if (agent.contains("QQBrowser")) {
//因为我们无法处理浏览器兼容问题,所以我们用一个输出语句代替处理
System.out.println("您用的是QQ浏览器");
} else if (agent.contains("Firefox")) {
System.out.println("您用的是火狐浏览器");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
通过不同浏览器访问servlet可以有不同的效果
演示案例3:获取referer
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Referer
String referer = req.getHeader("referer");
System.out.println(referer);
if (referer.contains("/demo")) {
System.out.println("播放音乐");
//等学了response之后就可以把输出语句中的内容显示到网页中
} else {
System.out.println("滚");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
注意:如果直接通过/demo/req4 访问这个Servlet,就相当于直接自己访问自己,referer 的值是null,所以我们需要在index.jsp中加一个超链接,控制跳转
4.3.4 获取请求体数据
- 请求体:只有POST方式有请求体。在请求体中封装了POST请求的请求参数
- 步骤:
- 获取流对象
- BufferedReader getReader() : 获取字符输入流,只能操作字符数据
- ServletInputStream getInputStream() : 获取字节输入流,它可以操作所有类型数据(文件上传时讲解)
- 从流中获取数据
- 获取流对象
演示案例:
创建regist.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/demo/req5" method="post">
用户名:<input type="text" placeholder="请输入用户名" name="username"><br>
密码:<input type="password" placeholder="请输入密码" name="username"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
创建servlet
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求消息体
//1. 获取字符流
BufferedReader br = req.getReader();
//2.读取数据
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);//username=aaa&username=aaa
}
}
}
4.3.4 其他方法
这些方法是由前面基础方法衍生出来的一些通用方法,更方便使用
1.获取请求参数对象(GET/POST都能用)
- String getParameter(String name): 根据参数名称获取参数值
- String[] getParameterValues(String name): 根据参数名称获取参数值的数组(多用于复选框)例如:hobby=java &hobby=c
- Enumeration getParameterNames():获取所有请求的参数名称
- Map getParameterMap():获取所有参数的map集合
POST中文乱码解决:
//在获取参数前,设置req编码集
req.setCharacterEncoding("utf-8");
2. 请求转发
- **概述:**一种在服务器内部的资源跳转方式。
简单理解服务器内部的资源跳转:一个服务器中有多个Servelt,多个Servlet分工协作完成用户的一个请求,这多个Servlet间数据的交换,就是资源跳转
- 步骤:
- 通过request对象 获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
- 使用转发器对象进行转发:forward(ServletRequest request, ServletResponse response)
4. 获取ServletContext对象
- 方法:ServletContext getServletContext()
5.HTTP-相应消息
- 请求消息:客户端发送给服务器端的数据
数据格式:
- 相应消息:服务器端相应客户端的数据
数据格式:
- 相应行
- 相应头
- 相应空行
- 相应体体
5.1 响应消息的字符串格式:
通过获取浏览器 F12 查看 格式
图示:
HTTP/1.1 200 OK //响应行
// ****************************** 响应头 *****************************
Bdpagetype: 1
Bdqid: 0x86f2f3e6001eefab
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Wed, 20 Jan 2021 18:07:52 GMT
Expires: Wed, 20 Jan 2021 18:07:52 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=12; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=33425_33430_33344_33286_33395_33398_33334_26350; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 161116607205735506029724102714616377259
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
// ****************************** 响应头结束 *****************************
//响应空行
//响应体:就是整个页面的内容(包括:html,css,js, 及数据)
5.2 响应行
- 组成:
协议/版本 响应状态码 响应码描述
- 状态码:
状态码都是3位数字 服务器告诉客户端本次 请求 和 响应的状态
-
分类
- 1xx - 服务器接收客户端消息,但是没有接收完成。等待一段时间后,发送1xx状态码,询问客户端是否继续发送消息
- 2xx:成功。代表码:200
- 3xx: 重定向。资源跳转的方式。代表码:302-重定向 ,304-访问缓存
- 重定向:比如你要访问ServletA, A说它做不了,让你访问B,然后浏览器接收到A的相应之后,会自动的访问ServletB,就相当于你一次回车进行了两次请求。
- 4xx: 客户端错误。比如访问路径有问题,没有对应资源 ,就会报404。
- 5xx: 服务器端错误。 如果服务器代码出现异常就会报500
5.3 响应头
-
组成:
头名称 :值
-
常见响应头:
- Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
- Content-disposition:服务器告诉客户端以什么格式打开响应体数据。如果没有设置就使用默认值。
- 值:
- in-line:默认值,在当前页面内打开
- attachment ; filename=xxx:以附件形式打开响应体。即文件下载 filename执行下载文件名称
- 值:
6. Response 对象
6.1 概述
Response 对象是用来设置响应消息的
-
设置响应行
- 设置状态码:setStatus( int sc) - sc 为状态码
-
设置响应头
- setHeader(String name , String value)
-
设置响应体:
- 步骤:
- 获取输出流
- 字节输出流:ServletOutputStream getOutputStream()
- 字符输出流: PrintWriter() getWriter()
- 使用输出流将数据输出到客户端浏览器
- 获取输出流
我们通过下面4个小案例来讲解上述内容
- 步骤:
- 完成重定向
- 服务器输出字符数据到浏览器
- 服务器输出字节数据到浏览器
- 验证码
6.2 重定向
图解:
步骤:
1. 设置状态码为302
2. 设置响应头Location
简化操作:
由于 302 和 location为固定参数,所以将重定向简化为方法:sendRedirect(“/虚拟路径/资源路径”)
演示案例:responseDemo 通过重定向 访问 responseDemo2
responseDemo
/**
* 重定向演示案例
*/
@WebServlet("/responseDemo")
public class ResponseDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("responseDemo 被访问");
//设置状态码
resp.setStatus(302);
//设置响应头
resp.setHeader("location","/book/responseDemo2");
//重定向简单的方法实现:因为 302 和 location是固定的。
resp.sendRedirect("/book/responseDemo2");
}
}
responseDemo2
@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("responseDemo2 被访问");
}
}
重定向的特点:
- 转发的特点:forward
- 地址栏 路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求 可以使用request对象共享数据
- 重定向的特点:redirect
- 地址栏发生变化
- 可以访问其他服务器的资源
- 重定向是两次请求,不可以使用request对象共享数据
- 面试题: forward 和 redirect 的区别
路径写法:
-
路径分类
-
相对路径:找到当前资源和目标资源之间的相对位置关系
./
开头../
表示上一级目录
-
绝对路径:通过绝对路径可以确定唯一资源
- 以
/
开头。因为当前项目中localhost:8080/
虚拟路径都是统一的,所以可以省略为/
- 判断路径给谁用
- 浏览器用,要加虚拟目录 。例子:重定向
- 服务器用,不用加虚拟目录。例子:转发
- 使用request.getContextPath()动态获取项目虚拟路径,这样即便是修改项目虚拟目录,我们之前写的重定向方法也不会失效。
- 以
-
6.3 服务器输出字符数据到浏览器
演示案例:
/**
* 响应字符数据到浏览器
*/
@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取流
PrintWriter writer = resp.getWriter();
//输出字符
// writer.write("Hello Response");
//输出html标签
writer.write("<h1>Hello Response</h1>");
writer.write("你好 response");//乱码
}
}
演示案例2:处理中文乱码
/**
* 处理中文乱码
*/
@WebServlet("/res4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
分析:中文乱码问题
qq浏览器查看网页源码:默认编码集GBK
输出流通过tomcat获取。编码集应该是ISO-8859-1
*/
//解决中文乱码
//1. 获取流之前设置编码集
//resp.setCharacterEncoding("GBK");
/*
但是仅仅设置输出流的编码集,万一用户的编码集被修改不是默认的GBK呢?
我们可以使用相应头content-type,告诉浏览器本次相应的编码格式,浏览器读取到后,就会使用相应的编码
*/
//设置响应头的同时也设置了本身的编码集,所以resp.setCharacterEncoding()可以省略不写
//resp.setHeader("content-type","text/html;charset=utf-8");
//简单的方法设置编码
resp.setContentType("text/html;charset=utf-8");
//获取流
resp.getWriter().write("哈哈哈哈");
}
}
6.4 服务器输出字节数据到浏览器
演示案例:
/**
* 服务器相应字节数据:
* 一般这种方法我们不用在输出字符上,而是用在输出图片上,比如说验证码。
*/
@WebServlet("/res5")
public class ResponseDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("你好".getBytes("utf-8"));
}
}
6.5 验证码
**本质:**它是一张图片
**目的:**提高安全性,防止恶意注册等操作。
**方式:**随机生成
本次案例做的是比较初级的验证码:在内存中随机生成验证码
演示案例:
- 后台生成图片
import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/code")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.创建一个对象,代表内存中的一张图片(空白画布)
int width = 100;
int height = 50;
/*
new BufferedImage(参数1,参数2,参数3)
参数1:宽
参数2:高
参数3:图片类型 -常用 BufferedImage.TYPE_INT_RGB
*/
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//2.美化图片,让它变成我们想要的样子
//2.1 填充背景色
Graphics g = image.getGraphics();//获取画笔工具
g.setColor(Color.ORANGE);//设置画笔颜色
g.fillRect(0,0,width,height);//填充一个矩形
//2.2 画边框
g.setColor(Color.green);//设置画笔颜色
// g.drawRect(0, 0, 100, 50);//因为边框本身占一个像素,所以右边和下边的边框会超出
g.drawRect(0,0,width-1,height-1);
//2.3 验证码所有可能出现的字符
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
//2.4 获取随机数根据随机数取字符
for (int i = 1; i <= 4; i++) {
int index = new Random().nextInt(str.length());
char ch = str.charAt(index);
//2.5 写验证码
g.drawString(ch+"",width/5*i,height/2);
}
//2.6 画干扰线
g.setColor(Color.BLUE);
//两点确定一条线,一条线由x,y左边的组成。一共画十条
for (int i = 0; i < 10; i++) {
int x1 = new Random().nextInt(width);
int x2 = new Random().nextInt(width);
int y1 = new Random().nextInt(height);
int y2 = new Random().nextInt(height);
g.drawLine(x1,x2,y1,y2);
}
//3. 将图片输出到页面展示
ImageIO.write(image, "jpg", resp.getOutputStream());
}
}
- 前端页面展示验证码,并通过单击改变验证码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<script>
window.onload=function (){
//获取图片标签对象
var img = document.getElementById("checkCode");
//绑定单击时间
img.onclick = function () {
//获取时间戳,为了让浏览器不读取缓存图片
var date = new Date().getTime();
//重新请求
img.src="/book/code?"+date;
};
}
</script>
</head>
<body>
<input type="text" name="username">
<img id="checkCode" src="/book/code"/>看不清,换一张
</body>
</html>