Web 应用开发
get 和 post 请求方法 (重点)
http 协议中定义的请求方法有 DELETE、HEAD、GET、OPTIONS、POST、PUT、TRACE
在 http 协议中的两种常见的传参方法 get/post,例如
get 和 post 的共同点:Get 提交和 post 提交都是将数据发送到服务器端,只是他们的提交的方式不一样
1、语义不同。其中 GET 一般用于获取/查询资源信息,而 POST 一般用于更新资源信息【RESTful】
2、实际上传输数据时 method 和 data 没有任何关系,但是 get 特定浏览器和服务器对 URL 长度有限制,一般采用的是数据包头的方式传输数据,只能传输 ASCII 编码字符,一般上限为 2K;post 采用数据包体的方式传输数据,可以传输二进制数据内容,理论上说没有上限,一般编程上限是 64K。
3、安全性问题,get 传递数据会被缓存,能够存储在浏览器的历史记录中,所以安全性差,传递敏感数据一般采用 post
4、从请求反应速度 get 比 post 效率高,get 要求服务器立即处理请求,而 post 请求可能形成一个队列请求。
Servlet 概述
Servlet 是一种运行在服务器端用于扩展服务器功能的服务器端组件技术【API】,是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容
public class HelloServlet implements Servlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("在 HelloServlet 实例化后执行的方法,在整个 Servlet 实例的生命周期中该方法执
行且只执行一次,一般用于初始化操作");
this.config=config;
}
@Override
public ServletConfig getServletConfig() {
System.out.println("提供 ServletConfig 对象,ServletConfig 对象是当前 Servlet 对象的参数配置");
return config;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("用于处理客户端的请求,服务器会将客户端的请求信息封装到 req 对象中,当前处
理程序生成的响应信息写入到服务器初始化好的 res 对象中");
PrintWriter out=res.getWriter();//获取响应输出流
out.flush();
out.close();
}
@Override
public String getServletInfo() {
return "对当前 Servlet 进行说明";
}
@Override
public void destroy() {
System.out.println("在当前 Servlet 对象销毁之前执行的方法,在整个当前 Servlet 对象的生命周期中
执行且执行一次,最早的设计初衷在于资源的回收");
}
}
定义 Servlet 类后需要进行配置,也就是将一个地址和当前 Servlet 建立对应关系。Servlet 没有对应的 main 方法的程序执行起始点,所以需要客户端通过地址触发 Servlet 的执行。配置 Servlet 的常见方法有 2 种:web.xml配置、注解配置
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 类和名称建立对应关系,Servlet 不是只能有一个对应的地址 -->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.yan.HelloServlet</servlet-class>
<!-- 可以针对 Servlet 配置当前 Servlet 的初始化参数,该参数会在服务器初始化 Servlet 时封装到
config 对象种 -->
<init-param>
<param-name>name</param-name>
<param-value>yanjun</param-value>
</init-param>
<!--配置当前 Servlet 的加载次序,就是在服务器启动时即使没有客户端访问,也要进行初始化 -->
<load-on-startup>12</load-on-startup>
</servlet>
<!-- 按照名称对应一个请求地址 -->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
</web-app>
注解使用
@WebServlet(name=“对应 servlet 配置的名称”,value = “/hello.did”)
public class HelloServlet implements Servlet {
-
urlPatterns 定义对应的 url 地址,value 实际上就是 urlPatterns 的别名
-
loadOnStartup 配置默认的加载次序,实现 Servlet 的自动加载,以提高第一次访问 Servlet 的效率
-
initParams 是 WebInitParam[]类型的,用于使用注解定义配置参数
Servlet 三生命周期
1、客户端请求该 Servlet;
2、服务器加载 Servlet 类到内存;Class.forName
3、服务器实例化并调用 init()方法初始化该 Servlet;
4、服务器调用 Servlet 对象中 service(),如果 Servlet 采用的是继承 HttpServlet 实现的,则会根据请求方法不同调用 doGet() 或者 doPost(),此外还有 doHead()、doPut()、doTrace()、doDelete()、doOptions();注意如果继承 HttpServlet 时,则不允许重新定义 service 方法,因为 HttpServlet 是采用模板模式的思想简化 Servlet编程
HttpServlet 的 service 方法实现
String method = req.getMethod(); //获取客户端的请求方法,例如 GET 或 POST
if (method.equals(METHOD_GET)) { //判断是否为 GET 请求
doGet(req, resp);
}
5、Servlet 对象并不会执行完毕后卸载,而是常住内存.如果有后续请求,则自动调用 service 方法而不需要重新加载。如果并发访问,服务器将以多线程的方式执行 service 方法。Servlet 默认采用单实例多线程的方式对外提供服务,但是各个服务器考虑到执行效率的性能问题,可能会出现一定数量的 Servlet 实例
6、当服务器关闭或者内存不足而 Servlet 对象需要销毁时,首先调用 destroy()方法。destroy 方法在整个 Servlet对象的生命周期种运行且只运行一次
Servlet 编程接口
1、init()方法:在 Servlet 的生命期中,仅执行一次 init()方法。它是在服务器装入 Servlet 时执行的。可以配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init()。
public abstract void init(javax.servlet.ServletConfig arg0) throws javax.servlet.ServletException
2、service()方法方法是 Servlet 的核心。每当一个客户请求一个 HttpServlet 对象,该对象的 service()方法就要被调用,而且传递给这个方法一个请求 ServletRequest 对象和一个响应 ServletResponse 对象作为参数。 在HttpServlet 中已存在 service() 方法。缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。例如如果HTTP 请求方法为 GET,则缺省情况下就调用 doGet()
3、destroy() 方法仅执行一次,即在服务器停止且卸装 Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。例如,如果 Servlet 在运行时会累计统计数据,则可以编写一个 destroy() 方法,该方法用于在未装入Servlet 时将统计数字保存在文件中
JavaEE 为了规范 Servlet 编程提供的 Servlet 接口,但是直接使用 Servlet 接口比较繁琐。为了简化开发 JavaEE提供了 GenericServlet 一般用于非标准协议开发,例如游戏的服务器端应用开发;如果针对标准协议 http 的web 应用开发提供了 HttpServlet 类客户端请求的是服务器,为了屏蔽了底层细节,方便应用开发,所以按照 Servlet 规范可以使用一组相关的 API,
但是不需要了解这些 API 对象的创建。
SPI 和 API
1、config 对象
容器初始化一个 servlet 时,会为这个 servlet 建一个唯一的 ServletConfig。容器从 web.xml 或者注解中读出Servlet 初始化参数,并把这些参数交给 ServletConfig,然后把 ServletConfig 传递给 servlet 的 init(ServletConfig config)方法。也就是说容器只有在创建 servlet 实例时才会读 web.xml 文件中的 init-param,并且在 servlet 一生中只读一次
String parameter = config.getInitParameter("配置的初始化参数名称");
Enumeration<String> names = config.getInitParameterNames();//获取可以用于遍历所有初始化参数的枚举
器
while(names.hasMoreElements()){
String name=names.nextElement();
String value=config.getInitParameter(name);
}
ServletContext application = config.getServletContext(); 一个具体的应用对应一个全局的 application 对象。
注意如果需要修改 application 中存储的数据则需要考虑线程安全问题
String name = config.getServletName();获取当前 Servlet 的配置名称<servlet-name>
2、request 对象
request 对象是在服务器调用 service 方法时传入到当前 Servlet 对象中,是 ServletRequest 类型,可以用于封装用户请求信息
数据部分:header 请求头、请求参数 parameter、属性参数 attribute
Enumeration<String> names = request.getHeaderNames();
while(names.hasMoreElements()){
String name=names.nextElement();
String value=request.getHeader(name);
System.out.println(name+"-->"+value);
}
请求头中其它类型数据的获取
int header = request.getIntHeader("");
long dateHeader = request.getDateHeader(""); //日期类型的数据,注意返回值实际上是日期的毫秒值
Date dd=new Date(dateHeader);//将毫秒值转换为日期类型数据
通过 parameter 获取客户端请求时传递的数据时不区分请求方法
针对乱码问题的解决方案
1、针对 GET 请求:在服务器上进行配置 URIEncoding=UTF-8,具体使用的编码字符集 UTF-8 应该和页面的编码字符集一致
2、针对 POST 请求:在接收用户请求之前首先执行 request.setCharacterEncoding(“UTF-8”); 其中的编码字符集名称必须和页面的编码字符集一致
3、通用解决方案:
String username=request.getParameter("username");
byte[] arr=username.getBytes(); 首先使用系统默认的编码字符集针对 String 转换为字节数组
username=new String(arr,"UTF-8"); 使用指定的编码字符集重新将字节数组转换为 String
System.out.println(username);
总结:一般注意使用 UTF-8 编码可以避免出现各种问题
String username = request.getParameter("username");
String[] hobbies = request.getParameterValues("hobby");//用于使用一个名称传递多个数据,例如复选框
Enumeration<String> names = request.getParameterNames(); //所有请求参数的枚举
Map<String, String[]> map = request.getParameterMap(); //获取所有的请求参数,键值对样式
理论上来说,请求参数属于只读型
在编程中使用 attribute 可以实现在当前页面或者多个页面之间的数据传递。注意:如果使用 attribute 跨页面数据传递,必须是请求转发,否则数据丢失
request.setAttribute("name",123); //向 request 的 attribute 种存储数据
request.setAttribute("name",666); //修改数据,attribute 可以理解为一个 Map 容器,如果重名则后盖前
Object attribute = request.getAttribute("name"); //获取 attribute 种的数据
if(attribute!=null && attribute instanceof Integer){
Integer kk=(Integer) attribute;
}
Enumeration<String> attributeNames = request.getAttributeNames();//枚举器
request.removeAttribute("name");//删除数据
和地址相关的操作
RequestDispatcher dispatcher = request.getRequestDispatcher("path");可以使用 dispatcher 对象实现请求转
发和包含操作
请求的测试路径 localhost:8080/test/test.do?id=100&age=18&name=zhangsan
RequestDispatcher dispatcher = request.getRequestDispatcher("path");
String contextPath = request.getContextPath(); //获取请求的上下文路径,例如 jetty 种配置的/test
System.out.println(contextPath);
String method = request.getMethod();//获取请求方法,例如 GET、POST
String queryString = request.getQueryString();//获取查询字符串,实际上就是请求路径种?后面的内容,例如
id=100&age=18&name=zhangsan
System.out.println(queryString);
String requestURI = request.getRequestURI(); //URI 统一资源表示符,例如/test/test.do
StringBuffer requestURL = request.getRequestURL(); //URL 统一资源定位器,
http://localhost:8080/test/test.do
System.out.println(requestURI+"-->"+requestURL);
//获取服务器的相关信息,注意:如果租用别人的服务器最好不要写探针程序
String serverName = request.getServerName(); //localhost
System.out.println(serverName);
int serverPort = request.getServerPort(); //8080
System.out.println(serverPort);
String localAddr = request.getLocalAddr();
String localName = request.getLocalName();
int localPort = request.getLocalPort();
System.out.println(localAddr+"<-->"+localName+"<-->"+localPort);
//获取客户端的相关信息,在 BBS 类型的应用种使用较多
String remoteAddr = request.getRemoteAddr(); //*** [0:0:0:0:0:0:0:1],一般用于获取客户端的信息[客户端
机器名/IP 地址]
System.out.println(remoteAddr);
String remoteHost = request.getRemoteHost();
System.out.println(remoteHost);
int remotePort = request.getRemotePort();// 52591
System.out.println(remotePort);
3、response 对象
ServletResponse 对象用于动态响应客户端请示,控制发送给用户的信息,并将动态生成响应。
设置响应头
request.setHeader/setIntHeader/setDateHeader
response.setHeader(“Refresh”,“5;URL=https://www.baidu.com”); 如果没有 URL 则表示自动刷新访问当前地址,如果有 URL 则表示 5 秒钟后自动跳转到新地址 URL
禁止使用缓存
response.setDateHeader(“Expires”, 0); 过期时间设置
response.setHeader(“Cache-Control”, “no-cache”); 针对 http1.0 和 http1.1 协议中关闭缓存
response.setHeader(“Pragma”, “no-cache”);
设置缓存时间 100 秒
response.setDateHeader(“Expires”, System.currentTimeMillis()+100*1000);
重定向
response.sendRedirect("/bbb.do");
response.sendError(404,"小结爱吃火锅");//参数 1 为错误状态代码,具体值是在 http 协议种规范定义的,参
数 2 为报错信息。针对报错处理浏览器提供默认的简单页面
统一错误处理 web.xml
<error-page> 可以定义多个,可以使用通配符,针对不同的错误状态码使用不同的页面显示
<error-code>404</error-code>
<location>/aaa.html</location>
</error-page>
<error-page>可以定义多个,在应用种出现执行类型的异常则跳转对应的页面
<exception-type>java.lang.Exception</exception-type>
<location>/bbb.html</location>
</error-page>
输出流
ServletOutputStream outputStream = response.getOutputStream(); //字节输出流
PrintWriter writer = response.getWriter(); //字符输出流
4、session 对象
HttpSession 是 Java 平台对 session 机制的实现规范,因为它仅仅是个接口,具体到每个 web 应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异
session 对象提供的方法
获取 session 的方法:HttpSession session=request.getSession(); 如果和当前用户有关联的 session,则获取和当前用户关联的 session;如果没有 session 则新创建一个 session 对象机器内存不可能是海量的,所以 session 不能一致存在,所以 session 有一个超时配置,如果超时则自动销毁
web.xml
<session-config>
<session-timeout>30</session-timeout> 注意单位为分钟
</session-config>
超时时间在 Tomcat 中默认为 30m,如果希望给用户友好,则增加时间;如果希望安全性高则减少配置的时间
常见方法
存取数据
setAttribute/getAttribute/removeAttribute
session 工作原理
session 依赖 Cookie,在 cookie 中存储 session 的编号 getId。
1、当客户端第一次访问服务器时,服务器会为客户端创建一个 session 对象,然后把 session 对象放到 session池中,在响应时把 sessionId 通过 Cookie 响应给客户端。注意,只有在第一次访问时,服务器才会创建 session,
给客户端响应 sessionId。
2、当客户端再次访问服务器时,会在请求中带着 sessionId 给服务器,服务器通过 sessionId 到 session 池中找到 session 对象,这就可以完成会话跟踪了。也就是说,服务器端保存的是 session 对象,而客户端只有sessionId。每次访问都需要通过客户端的 sessionId 来匹配服务器端的 session 对象!这样用户在 session 中保存的数据就可以再次被使用了。
3、sessionId 是服务器通过 Cookie 发送给客户端浏览器的,这个 Cookie 的 maxAge 为-1,即只在浏览器内存中存在。如果关闭所有浏览器窗口,那么这个 Cookie 就会消失了
Cookie 问题
Cookie 是一个客户端会话技术,是由服务器端创建,放在响应头发送到客户端保存,用于存储少量数据,因为存放在客户端中,容易被人编造伪造,不是很安全。一般不用于存储重要信息。它是通过键值对传递信息的。Cookie 可以分为内存 Cookie 和文件 Cookie。
1、只是进行了 new 操作的话,当用户退出 Browser 时,cookie 会被删除掉,而不会被存储在客户端的硬盘上
2、如果要存储 cookie,需加 cookie.setMaxAge(200)。Cookie 设置的生命周期单位是秒
由于浏览器一般只允许存放 300 个 Cookie,每个站点最多存放 20 个 Cookie,每个 Cookie 的大小限制为 4KB,因此 Cookie 不会塞满硬盘,更不会被用作拒绝服务攻击手段。
5、应用上下文 application
在 Java 语言中,能够独立运行的程序称为 Java 应用程序 Application。
ServletContext 是一个全局的储存信息的空间,服务器启动时创建,服务器关闭才释放。request 一个用户可有多个;session 一个用户一个;而 servletContext 是所有用户共用一个。所以为了节省空间、提高效率,ServletContext 中,要放必须的、重要的。但是所有用户需要共 享的线程又是安全的一些信息
配置web.xml
<context-param>
<param-name>appName</param-name>
<param-value>马氏集团管理系统</param-value>
</context-param>
上下文参数可以在当前应用中的任何位置通过 application 对象进行获取,是一个全局共享的变量jsp 页面中使用默认对象获取配置数据<%=application.getInitParameter(“appName”)%>>
在 servlet 中获取配置参数
ServletContext servletContext = this.getServletContext();
String name= servletContext.getInitParameter(“appName”);
web 页面之间的关系
按照标准,web 页面之间的关系有三种:请求转发、重定向和包含
请求转发:
RequestDispatcher requestDispatcher = request.getRequestDispatcher("hello.do");
requestDispatcher.forward(request,response);
特征:当前页面和跳转的目标页面共享 request 和 response 对象
重定向
response.sendRedirect(“bbb.do”);
特征:生成一个响应 302,要求浏览器针对新地址重新发起请求。新请求和原始请求没有任何关系
包含:用于实现在一个页面中包含另外一个页面
request.getRequestDispatcher(“in1.do”).include(request,response);
在 JSP 中包含可以分为两种:静态包含和动态包含
静态包含<%@ include file=“01.jsp”%>,先将被包含页面源代码拷贝到当前页面种,然后再编译,并解释执行
动态包含<jsp:include page=“01.jsp”>,每个页面各自独立执行,最种包含被包含页面生成的结果
Servlet 运行特征
1、HttpServlet 定义了用于支持有条件 GET 操作的 getLastModified 方法。所谓的有条件 GET 操作是指客户端通过 GET 请求获取资源时,当资源自第一次获取那个时间点发生更改后才再次发生数据,否则将使用客户端缓存的数据。在一些适当的场合,实现此方法可以更有效的利用网络资源,减少不必要的数据发送
2、Servlet 默认是线程不安全的,需要开发人员处理多线程问题。通常 Web 容器对于并发请求将使用同一个servlet 处理,并且在不同的线程中并发执行 service 方法
SingleThreadModel 接口的作用是保证一个特定的 servlet 实例的 service 方法在一个时刻仅能被一个线程执行,一定要注意,此包装仅适用于每一个 servlet 实例,因此容器可以选择池化这些对象。有些对象可以在同一时刻被多个 servlet 实例访问,如 HttpSession 实例,可以在一个特定的时间对多个 Servlet 可用,包括哪些实现了 SingleThreadModel 接口的 Servlet
3、Servlet 容器通过调用 Servlet 实例的 init 方法完成初始化,init 方法定义在 Servlet 接口中,并且提供一个唯一的 ServletConfig 接口实现的对象作为参数,该对象每个 Servlet 实例一个。配置对象允许 Servlet 访问由Web 应用配置信息提供的键值对的初始化参数。该配置对象也提供给 Servlet 去访问一个 ServletContext 对象,ServletContext 描述了 Servlet 的运行时环境
4、客户端请求由 ServletRequest 类型的 request 对象表示。Servlet 封装响应并返回给请求的客户端,该响应由 ServletResponse 类型的 response 表示。两个对象 request 和 response 是由容器通过参数传递到 Servlet接口的 service 方法的
5 、 在 HTTP 请 求 场 景 下 , 容 器 提 供 的 请 求 和 响 应 对 象 具 体 类 型 分 别 是 HttpServeltRequest 和HttpServletResponse。需要注意的是,由 Servlet 容器初始化的某个 Servlet 实例在服务期间,可以在生命周期中不处理任何请求
6、ServletException 异常处理
ServletException 表示在处理请求过程中发生了错误,容器应该采取适当措施清理请求
UnavailableException 表 示 servlet 不 能 处 理 请 求 , 可 能 是 临 时 的 也 可 能 是 永 久 的 。 如 果UnavailableException 表示的是一个永久性的不可用,Servlet 容器必须从服务中移除这个 Servlet,调用它的destory 方法,并释放 Servlet 实例
用户的 4 大领域对象
page 只在当前页面范围内有效
request 在请求转发时可以在多个页面之间共享数据
session 针对单一用户的跨页面数据共享
application 在当前应用范围内跨用户数据共享
跟踪用户 4 大方法
隐藏域
URL 重写 http://localhost:8080/dd.do?id=11
Cookie 用于实现客户端跟踪用户
session 用于实现服务器端跟踪用户,但是需要客户端的内存 Cookie 的支持,每次提交时需要传递一个 Session的编号,用于区分不同用户