Servlet总结
1.JavaEE
JavaEE、Servlet、Jsp、Web Container、tomcat这几者之间的关系是什么?
JavaEE是企业级应用开发规范,它本身并不是一种技术,而是多种技术解决方案的集合(即多种技术组件的集合),其中Servlet、JSP是JavaEE的两个主要组件。而Servlet、JSP程序需要放入Web容器中才能运行,Web Container、tomcat就是常见的容器。
2.Tomcat
目录
-
bin目录,存放运行Tomcat需要的命令文件,例如启动和关闭Tomcat的命令
win:startup.bat、shutown.bat
linux:startup.sh、shutdown.sh
-
conf目录,存放Tomcat相关的配置文件,其中最重要的是server.xml,例如可以在文件中配置Tomcat启动后监听的端口号
-
lib目录,存放项目运行时需要的jar包,例如,servlet-api.jar
-
logs目录,存放Tomcat运行时的日志文件
-
temp目录,存放Tomcat运行期间可能产生的临时文件
-
webapps目录,部署项目的位置
-
work目录,编译JSP页面后所存放的文件
虚拟目录:
-
在server.xml localhost标签下新增
弊端:修改srver.xml后需要重启服务器
-
在conf/Catalina/localhost目录下,新建映射名.xml,在xml中加入
不写path,path是xml名,不需重启服务器即可发布
3.HTTP
1.概念
hype text transport protocol超文本传输协议,规定了浏览器与服务器(万维网)之间通信的规则,是应用层协议。
2.交互
- 在浏览器地址栏输入URL,按下回车
- 浏览器向DNS服务器发送域名解析请求并获得目的ip地址和端口号
- 根据目的ip和端口号建立TCP连接
- 浏览器向服务器发送请求数据
- 服务器将网页内容响应给浏览器
- 浏览器将得到的数据,解析并显示
- 通信完成,断开TCP连接
3.请求内容
请求行,request line
消息报头(请求头),request head
请求正文(请求体),request body
4.响应内容
响应状态行,response status line
消息报头(响应头),response head
响应正文,response body
5.常见响应状态码
状态码 | 描述 |
---|---|
200 | 请求成功,已正常处理完毕 |
301 | 永久重定向 |
302 | 临时重定向 |
400 | 客户端内部错误 |
401 | 客户端请求没有经过授权 |
403 | 请求拒绝,没有访问权限IP被封) |
404 | 资源不存在 |
500 | 服务器内部错误 |
4. Servlet
按照Servlet规范要求,编写一个Servlet程序,只需实现javax.servlet.Servlet接口
1. javax.servlet.Servlet接口
它是Servlet技术的核心接口,所有的Servlet程序都必须是这个接口的实现类
package javax.servlet;
import java.io.IOException;
public interface Servlet {
//初始化servlet对象
void init(ServletConfig var1) throws ServletException;
//返回ServletConfig对象,该对象封装了Servlet的配置信息
ServletConfig getServletConfig();
//访问servlet对象的时候被调用(最重要的方法)
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
//返回servlet的相关信息,比如作者、版本、版权等等
String getServletInfo();
//销毁servlet对象
void destroy();
}
2. javax.servlet.GenericServlet
一个抽象类,实现了Servlet接口,但是没有实现(没重写方法体)最重要的service()方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package javax.servlet;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String message) {
this.getServletContext().log(this.getServletName() + ": " + message);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
return this.config.getServletName();
}
}
3.javax.servlet.HttpServlet
它是一个抽象类,但没有抽象方法
继承了GenericServlet,对于客户端的请求进行了细分:
- DELETE
- HEAD
- GET
- POST
- OPTIONS
- PUT
- TRACE
对于GET请求,使用doGet()方法进行处理。对于POST请求,使用doPost()方法进行处理
由此,我们只需编写子类继承HttpServlet并重写方法即可。
4.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">
<!-- servlet标签给Tomcat配置servlet程序 -->
<servlet>
<!-- 给servlet-name程序起一个别名 -->
<servlet-name>servletTest</servlet-name><--!第三步 -->
<!--servlet程序的全类名-->
<servlet-class>top.roud.servlet.doServlet2</servlet-class><--!第四步 -->
<!-- 表示多个servlet请求时此servlet的优先级,数字越小越优先执行 -->
<load-on-startup>1</load-on-startup>
<!-- 存放初始化参数 -->
<init-param>
<param-name>username</param-name>
<param-value>tom</param-value>
</init-param>
</servlet>
<servlet-mapping>
<!-- 通过地址找到servlet别名,再找到servlet程序 -->
<servlet-name>servletTest</servlet-name><--!第二步 -->
<!-- url-pattern 标签配置访问地址-->
<url-pattern>/do</url-pattern><--!第一步 -->
</servlet-mapping>
</web-app>
以上配置的效果与在Servlet类中注解的效果一致!!!
@WebServlet("/do")
5.生命周期
服务器创建servlet对象并自动调用service()方法
单例模式,存在线程安全问题,所以servlet程序尽量不使用成员变量
-
执行Servlet构造器方法
-
执行init()初始化方法
以上两步只在在第一次访问的时候创建Servlet对象会调用
-
执行service()方法,每次访问都会调用,每次客户向服务器发出请求时,服务器就会调用这个方法,这个方法会判断请求方式然后进行下一步执行
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
this.service(request, response);
}
4.执行destroy()销毁方法,在Web工程停止的时候调用
6.常用方法
//设置请求、响应的编码格式
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContextType("text/plain;charset=UTF-8");
//接收参数(单值、多值)
request.getParameter("参数名,及属性name的值");
///param?like=0&like=1
request.getParameter("like");
///param?name=tom&age=20&like=0&like=1
request.getParameterNames();
//获取参数名及值的映射
request.getParameterMap();
//写回数据
PrintWriter out = response.getWriter();
out.println("你好,程序员");
//HttpServletRequest接口下
//常用和路径相关的方法
getContextPath();//获取项目的根路径(默认是项目名)
getServletPath();//获取当前访问的Servlet地址
getRequestURL();//获取当前请求的URL地址
getRequestURI();//获取当前请求的URI地址
//ServletContext接口下
getContextPath();//获取项目的根路径(默认是项目名)
getRealPath(String Path);//获取一个资源在服务器中的绝对路径
getResourcePaths(String Path);//获取一个路径下面的所有资源
7.请求转发及重定向
请求转发
request.getRequestDispatcher(request,response);
request.setAttribute("testAttribute", new String[]{"proper", "xxx"});
request.getRequestDispatcher("/hello4").forward(request, response);
请求转发的特点:
-
使用request完成
-
url不变,是同一次请求
-
共享request域中的数据
-
可以转发到WEB-INF目录下(java Web应用下的安全目录,所谓安全是指客户端无法访问,服务端可以访问)
-
常用于页面控制,例如对失效或被冻结账户判断,再进行相应转发
如果request中有数据,在跳转到的下一个资源中是可以拿到这个数据,因为只有一个request请求对象
由于服务器跳转是把请求和响应在服务器内部中进行转发,所以浏览器的地址栏中的地址是不受影响的,始终还是第一次发出请求的地址,其实浏览器根本就不知道服务器内部的请求转发情况
在设置跳转的资源地址的时候,路径的前面一般要加上/,例如 String path = "/hello.html";
响应重定向
//通过后面拼接参数传递数据
response.sendRedirect("other?name=tom&age=20");
使用response完成
重定向的本质是把新的资源路径返回给浏览器,让浏览器向这个新地址发送一个新请求
此处是利用了响应的状态码302和响应头信息中的Location字段来完成
如果request中有数据,重定向后在新的资源中是拿不到这个数据的,因为重定向会发出新的请求,
但是数据在上一个老的请求中
由于重定向让浏览器发出新的请求,所以浏览器地址栏中的地址会变成新请求的地址
在设置重定向的资源地址的时候,路径的前面一般不加/
例如, String path = "hello.html"
8.路径
URL与URI的区别:
URL:统一资源定位符,不仅能标识资源,还能定位资源
URI:统一资源标识符
如URL:http://127.0.0.1:80/servlet/hello.html
此时URI:servlet/hello.html
**斜杠/ ** (非常重要)
如 /hello/hello.jsp
除第一个斜杠,其余斜杠代表路径分隔
第一个斜杠的含义:
1.客户端(浏览器)中
第一个斜杠代表端口号后的斜杠 http://ip:port/
此时/a.html代表http://ip:port/a.html
由于响应重定向是发送给客户端,所以前面一般加斜杠
2.服务端中,第一个斜杠表示项目名后的斜杠
如http://ip:port/servlet/
<!-- 当前url:http://127.0.0.1:8989/servlet-test/servlet/path-->
<a href="hello.html">点我</a>
<!-- 此连接为相对路径,此时path与hello.html在同一目录下,点击超链接后跳转至http://127.0.0.1:8989/servlet-test/servlet/hello-->
<a href="../hello2.html">点我2</a>
<!-- 此连接为相对路径,此时servlet与hello2.html在同一目录下,点击超链接后跳转至http://127.0.0.1:8989/servlet-test/hello2.html-->
HTML中标签,在HTML页面中添加base标签后,这个页面的所有相对路径,就不再是相对于地址栏中的路径了,而是相对于base标签中设置的路径
9. 修改路径
默认情况下,项目部署后,项目路径(ContextPath)就是项目的名字,也可以根据情况对该路径进行修改。
例如,把ContextPath修改为/
此时项目的ContextPath为 /servlet ,可以在这里直接修改为 /
启动后,原来是这样访问的路径:http://127.0.0.1:8989/servlet/index.html
现在可以这样来访问:http://127.0.0.1:8989/index.html
10.乱码
POST
//解决传参乱码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//解决写回数据乱码
resp.setContentType("text/plain;charset=UTF-8");
//或者
resp.setContentType("text/html;charset=UTF-8");
//或者
resp.setContentType("application/json;charset=UTF-8");
GET
//在server.xml种设置
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443"/>
//打散后组装
String name = request.getParameter("name");
name = new String(name.getByte("ISO8859-1","UTF-8"));
11.范围对象
接口类型 | 对象名 | 描述 |
---|---|---|
javax.servlet.http.HttpServletRequest | request | 同一个请求范围内 |
javax.servlet.http.HttpSession | session | 在同一个会话内 |
javax.servlet.ServletContext | application | 在同一个项目内 |
-
request对象
request、response是方法参数,可以直接使用,只在一次请求内有效
-
session对象
创建: 客户端与服务器无有效会话情况下,客户端访问服务器
销毁:超时自动销毁、session.invalidate()
- 一个session是指一个客户端与服务器的会话,并不是所有客户端共享
- request.getSession()方法第一次被调用会创建session对象,session.invalidate()调用会销毁session
- session超时的话也会自动销毁,默认超时时间为30分钟,其中超时时间是客户端不与服务器交互的时间
- 一个会话包含多次请求,所以放在session中的数据,可以被多个请求所共享
正常关闭服务器session会被序列化保存
session与cookie的区别:
session数据保存于服务端,生命周期仅为C/S无交互时间的30分钟,相对安全。应用场景:登录验证,如验证码
cookie数据保存于客户端,生命周期长(不被删除即存在),不安全完全可见,cookie为会话追踪技术
应用场景:判断用户是否登录过网站,以便下次登录可以直接登录,如果我们删除cookie,则每次登录必须重新填写登录的相关信息
3.application
启动服务器的时候,applicaion就会被创建,关闭服务器时,application就会被销毁
每个项目在运行期间,有且只有一个application与之对应
11.2范围对象存储数据
//存
request.setAttribute("key","value");
session.setAttribute("key","value");
application.setAttribute("key","value");
//取
request.getAttribute("key");
session.getAttribute("key");
application.getAttribute("key");
//删除
request.removeAttribute("key");
session.removeAttribute("key");
application.removeAttribute("key");
12. 会话追踪
12.1 无状态访问
HTTP的访问是无状态访问,当前的访问是不会知道之前访问的状态的。也就是说,http协议不会帮我们保存访问记录及痕迹。但是,有些时候我们需要记录之前的访问状态。这时候就需要使用到会话追踪技术。
12.2 cookie
cookie是常用到的一种会话追踪技术,cookie是浏览器中用来存储信息的一种本地文件,浏览器再向服务器发送请求的时候,可以根据服务器的地址,将对应的cookie中的信息,携带到请求头中,一起发送给服务器。
新建cookie对象,让response携带cookie发送给浏览器,浏览器会将cookie对象内容保存到本地对应的一个cookie文件中
//创建cookie对象
Cookie c1 = new Cookie("name","tom");
Cookie c2 = new Cookie("msg","hello");
//设置cookie的有效时间
c1.setMaxAge(60*60*24*365);
c2.setMaxAge(60*60*24*365*10);
//把cookie放到response里面
response.addCookie(c1);
response.addCookie(c2);
服务器读取cookie
servlet中,可以使用request对象拿到从浏览器发送过来的cookie数据
Cookie[] cookies = request.getCookies();
//遍历数组 拿出key和value
for(Cookie c:cookies){
String key = c.getName();
String value = c.getValue();
System.out.println(key+" : "+value);
}
12.3 cookie和session
在浏览器的cookie的文件中,有一个默认的名字:JSESSIONID之后,浏览器发送请求的时候,会把之前保存在cookie文件中的JSESSIONID的值,传给服务器。服务器通过这个JESSIONID的值,就能够知道服务器内存中是哪一个session对象,和当前这个客户端对应。
第二次访问页面时候,cookie会附带请求头(request)给服务器。服务器识别到这个cookie了。就会根据这个找对应的session,找到后先判断是否过期,没有过期就去读取session文件的配置
12.4 URL重写
当用户把浏览器的cookie功能禁用之后,浏览器在发请求的时候,响应(response)把cookie带回来,但是浏览器无法接收,这其中就包括最重要的JSESSIONID。
此时可以使用URL重写来达到会话追踪的效果。
String url = response.encodeURL("url");
注意,重写后的url和原来的url比较,其实就是在url后面拼接上了JSESSIONID的值,以便点击链接
的时候可以把JSESSIONID的值也传过来,从而达到会话追踪的效果
5. Servlet规范三大组件
三大组件:Servlet接口、Listener接口、filter接口
6.Filter
在一个请求去访问某个资源的时候,filter(过滤器)可以在这个请求,访问到这个资源之前,进行拦截,然后做出一系列的处理,例如编码转换、信息过滤、权限判断、登录验证等等,最后filter再决定,是否要让这个请求去访问那个目标资源。
package javax.servlet;
import java.io.IOException;
public interface Filter {
//过滤器对象初始化时调用的方法
public void init(FilterConfig filterConfig) throws ServletException;
//当过滤器拦截到请求的时候,会调用这个doFilter方法进行处理
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
//过滤器对象销毁时调用的方法
public void destroy();
}
filterChain过滤器链:在一个wed应用中,可以开发多个filter,这些filter组合起来称为过滤器链
web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用哪个filter,依次调用后面的过滤器。如果没有下一个过滤器,调用目标资源
如果是注解,执行顺序按注解中映射路径的首字母顺序执行
默认情况下,服务器内部跳转的请求,就无法被拦截,除非进行配置
<filter-mapping>
<filter-name>checkFilter</filter-name>
<url-pattern>/test</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
除此之外,还有其他的拦截类型,了解知道即可。重点关注的时候REQUEST和FORWARD俩种
Filter的生命周期
filtter的创建由服务器负责,web创建的时候就会创建filter实例化对象,并调用init方法进行初始化(filter创建和init方法执行都只进行一次)
每次filter进行拦截的时候,doFilter方法都会执行
当服务器关闭的时候,Filter对象会被销毁
权限拦截
...
//用户登录验证界面
//验证成功
request.getSession().setAttribute("user",user);
//拦截器
public class doFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//将ServletRequest转为HttpServletRequest
HttpServletRequest req= (HttpServletRequest) request;
HttpSession session = req.getSession();
LoginUser loginUser = (LoginUser) session.getAttribute("User");
if(loginUser!=null){
//放行到下一个拦截器或放行到资源
chain.doFilter(request,response);
}else{
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.println("未登录");
}
}
@Override
public void destroy() {
}
}
<filter>
<filter-name>loginFiler</filter-name>
<filter-class>top.roud.Filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFiler</filter-name>
<!--对于访问/JSP目录下所有资源的请求都进行拦截-->
<url-pattern>/JSP/*</url-pattern>
</filter-mapping>
7. Listener
观察者模式:一个被观察者所发出的信息,可能会影响到多个观察者。
被观察者与观察者之间是一对多的关系,当被观察者的状态发生改变时,通知所有的所有的观察者
7.1分类
监听对象为范围对象,根据监听类型分类
不同功能的监听器,需要实现不同的接口,可以大致分为以下几种监听器类型:
1、监听 request、Session、application 的创建和销毁,这些接口分别为:
ServletRequestListener
HttpSessionListener
ServletContextListener
2、监听request、session、application三个对象中属性变化,这些接口分别为:
ServletRequestAttributeListener
HttpSessionAttributeListener
ServletContextAttributeListener
3、监听Session对象里面存放着的其他对象,这些接口分别为:
HttpSessionBindingListener
HttpSessionActivationListener
7.2 使用
1.监听 request、Session、application 的创建和销毁
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class ListenerTest implements HttpSessionListener, ServletContextListener,
ServletRequestListener {
//参数为监听到的事件
// 创建 session
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println("新创建一个session, ID为: " + session.getId());
}
// 销毁 session
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println("销毁一个session, ID为: " + session.getId());
}
// 加载 application
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println("即将启动" + servletContext.getContextPath());
}
// 卸载 application
public void contextDestroyed(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println("即将关闭" + servletContext.getContextPath());
}
// 创建 request
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest)
sre.getServletRequest();
String uri = request.getRequestURI();
uri = request.getQueryString() == null ? uri : (uri + "?"+request.getQueryString());
request.setAttribute("dateCreated", System.currentTimeMillis());
System.out.println("IP " + request.getRemoteAddr() + " 请求 " + uri);
}
// 销毁 request
public void requestDestroyed(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest)
sre.getServletRequest();
long time = System.currentTimeMillis() - (Long)
request.getAttribute("dateCreated");
System.out.println(request.getRemoteAddr() + "请求处理结束, 用时" + time +"毫秒. ");
}
}
注意,这里的监听器使用了注解配置,也可以使用xml配置,在web.xml中
<listener>
<listener-class>com.briup.listener.TestListener</listener-class>
</listener>
2.监听request、session、application三个对象中属性变化
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
@WebListener
public class SessionAttributeListenerTest implements HttpSessionAttributeListener
{
//当属性发生修改事件会发生
// 添加属性
public void attributeAdded(HttpSessionBindingEvent se) {
//HttpSession session = se.getSession();
String name = se.getName();
Object value = se.getValue();
System.out.println("新建session属性:" + name + ", 值为:" + value);
}
// 删除属性
public void attributeRemoved(HttpSessionBindingEvent se) {
//HttpSession session = se.getSession();
String name = se.getName();
Object value = se.getValue();
System.out.println("删除session属性:" + name + ", 值为:" + value);
}
// 修改属性
public void attributeReplaced(HttpSessionBindingEvent se) {
HttpSession session = se.getSession();
String name = se.getName();
Object oldValue = se.getValue();
Object newValue = session.getAttribute(name);
System.out.println("修改session属性:" + name + ", 原值:" + oldValue + ",
新值:" + newValue);
}
}
8. error-page
在web.xml中,可以使用
例如,如果出现404情况,则跳转到/404.html页面中
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
也可以指定servlet中出现的异常情况:
例如,当servlet程序的时候,如果出现了以下类型异常,则跳转到not_found.html页面
<error-page>
<error-type>java.io.FileNotFoundException</error-type>
<location>/not_found.html</location>
</error-page>
9. WEB-INF
WEB-INF是Java的WEB应用中的安全目录。
安全目录指的是客户端无法访问,只有服务端可以访问的目录。
例如,如果将hello.html页面存放在WEB-INF下,那么浏览器中是无法通过以下地址【直接】访问到页
面的:
http://127.0.0.1:8989/servlet-test/WEB-INF/hello.html
但是,在servlet中是可以通过服务器内部跳转,来访问到WEB-INF目录下的资源的。
所以,浏览器可以访问servlet,让servlet再通过服务器内部跳转的方式,来访问WEB-INF下的
hello.html,从而提高页面的安全性,因为在servlet中可以对本次请求进行验证,通过后跳转到WEB-
INF下的页面,否则返回错误信息。
10. 上传
在不同版本的servlet中,完成上传的方式不同,servlet3.0及之前的版本,完成上传较为麻烦(了解即可),servlet3.1版本完成上传较为
简单。
servlet3.0之前上传需要注意事项:
-
需要导入两个包:commons-fileupload-1.4.jar,commons-io-2.8.0.jar
-
页面表单一定要是post方式提交
-
表单编码类型设置为:enctype="multipart/form-data",默认是 application/x-www-form-urlencoded
-
上传成功的时候,上传的文件可以在项目中的目录,也可以在系统目录
-
设置了表单的enctype="multipart/form-data" 就【无法】通过HttpServletRequest对象的
getParameter方法获取请求参数值。
servlet3.1上传需要注意事项:
- 不需要导入额外的jar包,使用servlet提供的@MultipartConfig注解和javax.servlet.http.Part接口类型
对象即可 - 页面表单一定要是post方式提交
- 表单编码类型设置为:enctype="multipart/form-data",默认是 application/x-www-form-urlencoded
- 上传成功的时候,上传的文件可以在项目中的目录,也可以在系统目录
- 设置了表单的enctype="multipart/form-data" 【也可以】通过HttpServletRequest对象的
getParameter方法获取请求参数值
servlet3.1实现:
-
导jar包
idea:动态WEB项目,在WED-INF下新建lib目录,将jar文件放入其中,file->Project Structure->Modules->Dependencies->+->...
-
编写页面upload.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>上传</title> </head> <body> <h1>hello world</h1> <h2>上传</h2> <form action="upload" method="post" enctype="multipart/form-data"> <input type="text" name="username" /><br> <input type="file" name="file" /><br> <input type="submit" value="上传" /> </form> </body> </html>
-
编写servlet
import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; /** * servlet3.1的上传操作 */ @WebServlet("/upload2")//注解映射路径 @MultipartConfig(location = "D:\\briup\\upload")//注解上传文件保存的位置 public class UpLoadServlet2 extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //可以使用filter来进行编码设置 //request.setCharacterEncoding("UTF-8"); //response.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); //获取上传的文件 Part part = request.getPart("file"); //获取上传文件的名字 //servlet3.1的方法 String fileName = part.getSubmittedFileName(); System.out.println(username+" 上传了文件:"+fileName); //避免上传的文件重复,文件名前面加上时间戳,part.write(“文件名”); part.write(System.currentTimeMillis()+"-"+fileName); } }
@MultipartConfig注解参数
Part 接口的方法:
11. 下载
-
编写download.html文件
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>下载</title> </head> <body> <h1>hello world</h1> <h2>servlet下载</h2> <a href="download?fileName=我的文档.txt">我的文档</a><br> </body> </html>
-
编写downloadServlet
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/download") public class DownLoadServlet extends HttpServlet { private static final long serialVersionUID = 1L; private String path = "D:\\briup\\upload"; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获得用户需要下载文件的名字,"我的文档.txt" String fileName = request.getParameter("fileName"); // 如果是下载文件的目录在项目中,可以通过该方法获得该下载文件目录的绝对路径 // String realPath = getServletContext().getRealPath(path); // 响应类型设置为下载:application/x-download response.setContentType("application/x-download"); // 下载文件时显示文件的保存名称 String fileDisplay = "briup_"+fileName; // 下载文件名的中文编码转换 因为写回去的时候是字节流 // 不设置会出现下载文件的中文名字乱码 fileDisplay = new String(fileDisplay.getBytes("utf-8"),"ISO-8859-1"); // 设置响应头部信息(http协议中的固定设置方式) response.addHeader("Content-Disposition", "attachment;filename="+fileDisplay); FileInputStream in = null; try { // 获取servlet中是输出流 ServletOutputStream out = response.getOutputStream(); // 使用IO流读取需要下载的文件 File file = new File(path,fileName); in = new FileInputStream(file); // 读取文件后再写回给浏览器 byte[] b = new byte[1024]; int len = -1; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } in.close(); } catch (Exception e) { e.printStackTrace(); }finally { if(in!=null) { in.close(); } } //注意下载之后就不能使用request跳转了,因为response已经返回了 } }
补充:
request.getQueryString(); 获取查询字符串
http://localhost/test.do?a=b&c=d&e=f 得到 :a=b&c=d&e=f