Servlet技术
一、Servlet技术
1、什么是Servlet
- Servlet是JaveEE规范之一,规范就是接口
- Servlet是JavaWeb三大组件之一。三大组件分别是:Servlet程序、Filter过滤器、Listener监听器
- Servlet是运行在服务器上的一个java小程序,它可以接收客户端发送来的请求,并响应数据给客户端
2、手动实现Servlet程序
- 编写一个类去实现Servlet接口
- 实现service方法,处理请求,并响应数据
- 到web.xml中去配置程序的访问地址
package com.first.cyh;
import jakarta.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
<?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">
<!--给Tomcat配置Servlet程序-->
<servlet>
<!--给Servlet程序起一个别名,一般是类名-->
<servlet-name>HelloServlet</servlet-name>
<!--Servlet程序的全类名-->
<servlet-class>com.first.cyh.HelloServlet</servlet-class>
</servlet>
<!--给Servlet程序配置访问地址-->
<servlet-mapping>
<!--告诉服务器我当前配置的地址给哪个Servlet程序用-->
<servlet-name>HelloServlet</servlet-name>
<!--
配置访问地址
/斜杠在服务器解析时表示地址为http://ip:port/工程路径
/hello表示地址为http://ip:port/工程路径/hello
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
3、通过注解配置访问路径url-pattern
Servlet3.0支持注解配置——@WebServlet()
不用再编写xml文件,只需要在实现了Servlet的类加上该注解即可
- 一个servlet可以配置多个访问路径@WebServlet({"/hello","/hi",“nihao”})
- 路径定义规则(xxx可以为*,即通配符)
- /xxx
- /xxx/xxx:多层路径,目录结构
- *.do
通配符优先级低,会在匹配不到时才访问
4、url地址到Servlet程序的访问
5、Servlet的生命周期
- 执行Servlet构造器方法
- 执行init初始化方法
- 第一、二步是在第一次访问的时候创建Servlet程序时才调用
- 执行service方法
- 每次访问都会调用
- 执行destroy销毁方法
- 在web工程停止的时候调用
6、GET和POST请求的分发处理
package com.first.cyh;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
public class HelloServlet implements Servlet {
public HelloServlet() {
System.out.println("你好鸭");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("初始化成功咯!");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//ServletRequest和HttpServletRequest是两个接口,HttpServletRequest继承自ServletRequest
//此处的形式参数其实是HttpServletRequest接口的实现类,因为接口不能实例化,所以传进来的是实现类
//因为需要实现ServletRequest到HttpServletRequest的强制类型转换(父类到子类)
//只有父类对象是用子类构造方法生成的才能实现强制转换,所以传进来的参数应该是HttpServletRequest接口的实现类
//强制类型转换,因为HTTPServletRequest接口才有getMethod方法来判断请求的类型
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
if(method.equals("GET")){
doGet();
}
else if("POST".equals(method)){
doPost();
}
}
private void doGet() {
System.out.println("我是GET请求");
}
private void doPost() {
System.out.println("我是POST请求");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("拜拜QAQ");
}
}
7、通过继承HttpServlet实现Servlet程序
一般在实际项目开发中,都是使用继承HttpServlet类的方式去实现Servlet程序
- 编写一个类去继承HttpServlet类(Servlet是接口,HttpServlet是类)
- 根据业务需要重写doGet()或doPost()方法
- 到web.xml中配置Servlet程序的访问地址
package com.first.cyh;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello doPost");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello doGet");
}
}
<?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">
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.first.cyh.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!--新增部分-->
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>com.first.cyh.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
</web-app>
8、使用IDEA创建Servlet程序
- 项目结构->Facets->点开Web工程->在底部“根源”处把选项勾上
- 在需要创建Servlet程序的软件包处右键新建->Servlet->为程序命名->去掉创建注解类选项的勾
- 配置文件自动生成改Servlet程序的配置->手动配置该Servlet程序访问路径
- 重写Servlet程序的doGet()和doPost()方法
9、Servlet的继承体系
Servlet接口
Servlet继承体系
二、ServletConfig类
ServletConfig类是Servlet程序的配置信息类
二者都是由Tomcat负责创建,我们负责使用
Servlet程序默认是第一次访问时创建对象,而ServletConfig是每个Servlet程序创建时就创建一个对应的ServletConfig对象
1、ServletConfig类的三大作用
- 可以获取Servlet程序的别名servlet-name的值
- 获取初始化参数init-param
- 获取ServletContext对象
<!--给Tomcat配置Servlet程序-->
<servlet>
<!--给Servlet程序起一个别名,一般是类名-->
<servlet-name>HelloServlet</servlet-name>
<!--Servlet程序的全类名-->
<servlet-class>com.first.cyh.HelloServlet</servlet-class>
<!--init-param是初始化参数-->
<!--新增部分-->
<!--可以配置多个参数-->
<init-param>
<!--参数名-->
<param-name>username</param-name>
<!--参数值-->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql//localhost/test</param-value>
</init-param>
<!--新增部分-->
</servlet>
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("初始化成功咯!");
//获取配置文件中servlet-name的值
System.out.println("servlet程序的别名是" + servletConfig.getServletName());
//获取配置文件中的初始化参数init-param
System.out.println("初始化参数username的值是" + servletConfig.getInitParameter("username"));
System.out.println("初始化参数url的值是" + servletConfig.getInitParameter("url"));
//获取ServletContext对象
System.out.println(servletConfig.getServletContext());
}
//其他方法省略
}
//输出结果
/*
初始化成功咯!
servlet程序的别名是HelloServlet
初始化参数username的值是root
初始化参数url的值是jdbc:mysql//localhost/test
org.apache.catalina.core.ApplicationContextFacade@5cd687b4
*/
2、注意事项
如果Servlet程序继承HttpServlet类时重写了init(ServletConfig config) throws ServletException方法,需要加上super.init(config)
//这是GenericServlet类(HttpServlet类的父类)中的方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public ServletConfig getServletConfig() {
return this.config;
}
public class HelloServlet2 extends HttpServlet {
@Override
//Tomcat生成ServletConfig对象并调用init程序
public void init(ServletConfig config) throws ServletException {
//super.init(config);
//如果没有调用父类的init(config)方法,则Tomcat创建的ServletConfig值丢失
//即config属性(定义在GenericServlet类中)没有保存值
System.out.println("doSth");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello doPost");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello doGet");
//由于GenericServlet类中的config没有保存值,所以getServletConfig()方法出现空指针异常
ServletConfig servletConfig = getServletConfig();
System.out.println("Servlet程序的别名为" + servletConfig.getServletName());
}
}
三、ServletContext类
1、什么是ServletContext
代表整个web工程,可以与程序的容器(Tomcat服务器)通信
- ServletContext是一个接口,它表示Servlet上下文对象
- 一个web工程,只有一个ServletContext对象实例
- ServletConfig对象取决于有多少个Servlet程序(一一对应,只能访问到配置文件中属于自己的配置信息)
- 但ServletContext对象还能访问配置文件中上下文参数
- ServletContext对象是一个域对象
- ServletContext对象是在web工程部署启动的时候创建,在web工程停止的时候销毁
什么是域对象
域对象指的是可以像Map一样存取数据的对象
这里的域指的是存取数据的操作范围——整个web工程
存数据 | 取数据 | 删除数据 | |
---|---|---|---|
Map | put() | get() | remove() |
域对象 | setAttribute() | getAttribute() | removeAttribute() |
2、获取ServletContext对象的方法
- 通过HttpServletRequest对象获取
- request.getServletContext();
- 通过HttpServlet获取
- this.getServletContext();
- 通过servletConfig对象获取
- servletConfig.getServletContext();
@WebServlet("/getcontext")
public class GetContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过HttpServlet获取
ServletContext servletContext1 = this.getServletContext();
//通过HttpServletRequest req对象获取
ServletContext servletContext2 = req.getServletContext();
//通过servletConfig对象获取
ServletContext servletContext3 = this.getServletConfig().getServletContext();
System.out.println(servletContext1);
System.out.println(servletContext2);
System.out.println(servletContext3);
}
}
3、ServletContext类的四大作用
- 获取web.xml中配置的上下文参数context-param
- 获取当前工程路径,格式:/工程路径
- 获取工程部署在服务器硬盘上的绝对路径
- 像Map一样存取数据
1)获取信息
<?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">
<!--新增部分-->
<!--context-param是上下文参数(它属于整个web工程),可以配置多组-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>
<!--新增部分-->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.first.cyh.HelloServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql//localhost/test</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
public class ContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取配置文件中的上下文参数
ServletContext context = getServletConfig().getServletContext();
System.out.println(context.getInitParameter("username"));
System.out.println(context.getInitParameter("password"));
//获取当前的工程路径,格式:/工程路径
System.out.println(context.getContextPath());
//获取工程部署后在服务器硬盘的绝对路径
/*
getRealPath()返回一个给定虚拟路径的真实路径
这个斜杆表示站点的根,也就是获取网站的根目录,即工程部署路径
而/css就是表示该路径下的css文件夹
*/
System.out.println(context.getRealPath("/"));
System.out.println(context.getRealPath("/css"));
System.out.println(context.getRealPath("/jpg"));
}
}
上述程序执行后会返回
context
root
/myfirst
E:\eclipse-workspace\Javaweb-servlet\out\artifacts\Javaweb_servlet_war_exploded
E:\eclipse-workspace\Javaweb-servlet\out\artifacts\Javaweb_servlet_war_exploded\css
E:\eclipse-workspace\Javaweb-servlet\out\artifacts\Javaweb_servlet_war_exploded\jpg
而为什么获得的路径为什么和我们项目创建的路径不一样,在我的另一篇博文(web工程部署路径与getServletConfig().getRealPath()_得过且过的勇者y的博客-CSDN博客中详细讲解了
为什么需要获取工程存放路径呢?
因为项目在本地开发后,项目中的文件路径写的是本地的路径。然而当项目部署到其他设备后,物理地址发生改变,就会导致文件找不到而报错。
2)存取数据
public class ContextServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//GenericServlet获取获取ServletContext对象的方式,等同于getServletConfig().getServletContext()
ServletContext servletContext = getServletContext();
System.out.println("保存之前Context1中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
//存数据
servletContext.setAttribute("key1","value1");
//取数据
System.out.println("Context1中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
}
}
public class ContextServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
System.out.println("Context2中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
}
}
以下为两次访问ContextServlet1输出的结果(没有重新部署)
保存之前Context1中获取域数据key1的值是:null
Context1中获取域数据key1的值是:value1保存之前Context1中获取域数据key1的值是:value1
Context1中获取域数据key1的值是:value1
重新部署后再次访问ContextServlet1输出的结果
保存之前Context1中获取域数据key1的值是:null
Context1中获取域数据key1的值是:value1
这是因为ServletContext对象是在web工程部署启动的时候创建,在web工程停止的时候销毁,所以没有重新部署的话key1在set之后就一直存在,所以第二次及以后访问时都会有保存之前Context1中获取域数据key1的值是:value1
,而重新部署之后第一次访问就又是null
而前文中的getInitParameter()获取的数据是保存在xml文件中的,故一直存在
总结
setAttribute方式存参数相当于把数据存在内存中,工程停止时消失,而xml方式存参数则是持久化到硬盘中
getInitParameter获得的是xml方式存储的参数
getAttribute获得的是setAttribute方式存储的参数
而当我们已经访问完ContextServlet1后再访问ContextServlet2,会输出
Context2中获取域数据key1的值是:value1
这是因为一个web工程,只有一个ServletContext对象实例,两个Servlet程序中的servletContext其实是相同的,如果我们在两个程序中都输出servletContext这个对象,值是相同的
所以当第一个Servlet程序设置完key1参数后,第二个自然也可以使用它
3)获取MIME类型
MIME类型是在互联网通信过程中定义的一种文件数据类型,格式为:大类型/小类型,如text/html,image/jpg
用处:响应头中content-type需要返回响应体的数据类型,这时候就要获取我们返回的数据的MIME类型
获取方法:通过文件后缀名在tomcat配置文件(tomcat的conf目录下的web.xml)中寻找对应的MIME类型(因为context对象可以与tomcat进行通信)
@WebServlet("/getmime")
public class GetMime extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//定义文件名
String filename = "a.jpg";
//通过文件名的后缀获取对应MIME类型
String mimeType = servletContext.getMimeType(filename);
System.out.println(mimeType);
}
}
返回image/jpeg
四、HttpServletRequest类
1、作用
每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的Http协议信息解析封装好到Request对象中,然后传递到service方法(doGet和doPost)中给我们使用。我们可以通过HttpServletRequest对象,获得到所有请求的信息
2、常用方法
- getRequestURI():获取请求的资源路径
- getRequestURL():获取请求的统一资源定位符(绝对路径)
- getRemoteHost():获取客户端的IP地址
- getHeader():获取请求头
- getParameter():获取请求的参数
- getParameterValues():获取请求的参数(多个值得时候使用)
- getMethod():获取请求的方式(GET或POST)
- setAttribute(key, value):设置域数据
- getAttribute(key):获取域数据
- getRequestDispatcher():获取请求转发对象
public class RequestAPIServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("URI-->" + req.getRequestURI());//URI-->/myfirst/req
System.out.println("URL-->" + req.getRequestURL());//URL-->http://localhost:8080/myfirst/req
//如果以localhost访问,则返回0:0:0:0:0:0:0:10:0:0:0:0:0:0:1
//以127.0.0.1访问,则返回127.0.0.1
//以真实ip访问,则返回真实ip
System.out.println("HOST-->" + req.getRemoteHost());
//HEADER-->Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30
System.out.println("METHOD-->" + req.getMethod());//GET
System.out.println("HEADER-->" + req.getHeader("User-Agent"));
}
}
public class RequestGetParameter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("用户名" + req.getParameter("username"));
System.out.println("密码" + req.getParameter("password"));
String[] hobbies = req.getParameterValues("hobby");
try{
System.out.println("兴趣爱好" + Arrays.asList(hobbies));
}catch (Exception e){
System.out.println(e.getStackTrace());
}
}
}
注意点
如果使用的是post请求,则需要设置字符集,否则在post请求参数是中文会出现乱码
req.setCharacterEncoding(“UTF-8”);
且此设置要在所有获取参数语句(不管会不会获取中文)的前面设置
只要有获取参数的语句在设置语句前面,不管这条语句会不会获取中文,都会导致设置失效,从而使那些在设置语句后的获取参数语句同样会乱码
3、请求转发
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//查看请求的带来的数据
String username = req.getParameter("username");
System.out.println("servlet1中请求带来的数据:" + username);
//处理业务
//doSth
username += "ahahha";
System.out.println("业务1完成,username=" + username);
//处理完业务,在request域中存储数据
req.setAttribute("username",username);
//询问servlet2的路径
//请求转发必须以斜杆开始,表示http://ip:8080/工程名/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
//带着请求走向servlet
requestDispatcher.forward(req,resp);
}
}
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//查看请求带来的数据
String username = request.getParameter("username");
System.out.println("servlet2中请求带来的数据:" + username);
//获取servlet1传入的数据
Object obj = request.getAttribute("username");
System.out.println("servlet1传入的数据:"+obj);
//处理自己的业务
System.out.println("业务处理完成");
}
}
请求转发的特点
- 浏览器地址栏没有发生变化
- 它们是一次请求
- 它们共享Request域中的数据
- 可以转发到WEB-INF目录下
- 不可以访问工程以外的资源
4、base标签的作用
设置页面相对路径工作时参照的地址,href属性就是参数的地址值
应用场景
如果页面采用请求转发,跳转页面时地址不会发生改变,这时候在跳转到的页面中如果访问了设置成相对路径的地址,则会发生请求错误,因为如果没有设置base标签,默认相对地址会参照于当前浏览器的地址
5、web中’/‘的不同意义
在web中,’/'是一种绝对路径
如果被浏览器解析,得到的地址是:http://ip:port/
<a href="/"></a>
如果被服务器解析,得到的地址是:http://ip:port/工程路径
<url-pattern>/servlet1</url-pattern>
servletContext.getRealPath("/")
request.getRequestDispatcher("/")
特殊情况:response.sendRediect("/")是把斜杠发给浏览器解析,故得到的是http://ip:port/
五、HttpServletResponse类
1、作用
HttpServletResponse类和HttpServletRequest类一样,每次请求进来,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息。我们如果需要设置返回给客户端的信息,都可以通过它来设置。
2、两个输出流的说明
- 字节流:getOutputStream()——常用于下载(传递二进制数据)
- 字符流:getWrite()——常用于回传字符串(常用)
两个流同时只能使用一个,否则会报错
3、如何往客户端回传数据
回传字符串
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置服务器字符集
response.setCharacterEncoding("UTF-8");
//设置浏览器字符集
response.setHeader("Content-Type","text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("哈哈");
}
设置字符集也可以使用以下方法
response.setContent-Type("text/html;charset=UTF-8")
此法会同时设置服务器和浏览器的字符集,并设置响应头
以上方法都应该在获取输出流之前调用
通过字节流回传字符串
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("txt/html;charset=utf-8")
PrintWriter writer = response.getOutputStream();
//获取字符串的字节数组
writer.write("哈哈".getBytes("utf-8"));
//默认编码是本机编码,也可以自己设置编码
}
4、请求重定向
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//第一种方法
/*
System.out.println("我曾来过");
//设置状态码302,表示重定向(已搬迁)
response.setStatus(302);
//设置响应头,说明新的地址在哪
response.setHeader("Location","http://localhost:8080/myfirst/response2");
*/
//第二种方法(推荐使用)
response.sendRedirect("https://www.baidu.com");
}
}
public class Response2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Content-Type","text/html;charset=UTF-8");
response.getWriter().write("欢迎您的到来");
}
}
请求重定向的特点
- 浏览器地址栏会发生变化
- 两次请求
- 不共享Request域中的数据
- 不能访问WEB-INF下的资源
- 可以访问工程外的数据
六、乱码问题详解
1、乱码原因
服务器编码和浏览器端解码使用的字符集不一致
浏览器端默认使用的编码和当前操作系统的语言环境有关,如果是中文的windows系统则默认是GBK或gb2312
如果服务器中字符流是自己new创建出来的,则也是与本机字符集一致,不会乱码
而当服务器通过response.getWriter()获取字符输出流,这个输出流是通过tomcat返回的,tomcat使用的编码是ISO-8859-1
2、解决方法
获取流对象之前,设置流的默认编码
response.setCharacterEncoding("utf-8");
告诉浏览器服务器发送的消息体数据的编码,建议浏览器使用该编码解码
response.setHeader("context-type", "txt/html;charset=utf-8")
简单形式
response.setContentType(“txt/html;charset=utf-8”);
七、验证码的实现
本质:图片
目的:防止恶意表单注册
步骤:
- 创建一个对象,在内存中生成图片(验证码图片对象)
- 美化图片
- 将图片输出到页面展示
@WebServlet("/checkcode")
public class CheckCode extends HttpServlet {
int width = 100;
int height = 50;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建验证码图片对象
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//美化图片
//填充背景色
Graphics g = image.getGraphics();//画笔对象
g.setColor(Color.PINK);//设置画笔颜色
g.fillRect(0,0,width,height);
//画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width-1,height-1);
String str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
Random random = new Random();
for(int i = 1; i <= 4; i++){
int index = random.nextInt(str.length());
//获取随机字符
char ch = str.charAt(index);
//生成验证码
g.drawString(ch+"",width/5*i,height/2);
}
//画干扰线
for(int i = 0; i < 6; i++){
int x1 = random.nextInt(width);
int x2 = random.nextInt(width);
int y1 = random.nextInt(height);
int y2 = random.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//将图片输出到页面展示
ImageIO.write(image,"jpg",resp.getOutputStream());
}
}
八、文件下载的实现
1、需求
- 页面显示超链接
- 点击超链接后弹出下载提示框或直接下载(取决于浏览器)
- 完成图片文件下载
2、分析
- 默认情况下:超链接指向的资源如果能被浏览器解析(如图片),则直接在浏览器中展示,如果不能解析(如视频),则弹出下载提示框(有的浏览器不会弹出,自动下载)
- 我们想要实现任何资源都弹出下载提示框
- 使用响应头设置资源的打开方式:
content-disposition:attachment;filename=xxx
(有的浏览器处理attachment不一定会弹出提示框)- attachment表示以附件的形式,filename表示文件名称
3、步骤
- 定义页面,编辑超链接href属性,指向servlet,传递资源名称filename
- 定义servlet
- 获取文件名称
- 使用字节输入流加载文件进内存
- 找到文件的服务器路径
- 用字节流关联
- 指定response的响应头
- 设置响应头类型
- 设置响应头打开方式
- 将数据写出到response输出流
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求参数,文件名称
String filename = req.getParameter("filename");
//获取servletContext对象以获取文件的真实路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/jpg/" + filename);
//用字节流关联文件
FileInputStream in = new FileInputStream(realPath);
//获取文件对应的MIME类型以设置响应头类型
String mimeType = servletContext.getMimeType(filename);
resp.setHeader("content-type",mimeType);
//设置响应头的打开方式
resp.setHeader("content-disposition","attachment;filename=" + filename);
//将输入流的数据写出到输出流中
ServletOutputStream outputStream = resp.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
//read()读取一个字节,read(byte b[])读取b.length个字节的数据到b数组中,并返回读取到的字节数,如果没有字节可读了,即read()督导文件末尾了,则返回-1
while((len = in.read(buff)) != -1){
//public void write(byte b[ ], int off, int len) :将参数b的从偏移量off开始的len个字节写到输出流
outputStream.write(buff,0,len);
}
in.close();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/myfirst/jpg/11.jpg">查看图片嗷</a>
<br>
<a href="/myfirst/download?filename=11.jpg">下载图片嗷</a>
</body>
</html>