Day47 Servlet、跳转方式、ajax交互
1. 软件构成和分层
2. Response
2.1 Http响应消息
*格式:
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Wed, 06 Jun 2018 07:08:42 GMT
(空行)
<html>
<head>
<title>$Title$</title>
</head>
<body>
hello , response
</body>
</html>
1) 响应行
1. 组成:协议/版本 响应状态码 状态码描述
2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
1. 状态码都是3位数字
2. 分类:
1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
2xx:成功。代表:200
3xx:重定向。代表:302(重定向),304(访问缓存)
4xx:客户端错误。
* 代表:
* 404(请求路径没有对应的资源)
* 405:请求方式没有对应的doXxx方法
5xx:服务器端错误。代表:500(服务器内部出现异常)
2) 响应头:
1. 格式:头名称: 值
2. 常见的响应头:
1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据
* 值:
* in-line:默认值,在当前页面内打开
* attachment;filename=xxx:以附件形式打开响应体。文件下载
3) 响应空行
2.2 Response对象
HttpServletResponse继承ServletResponse接口,专门封装HTTP响应消息
2.3 Response功能
- 设置响应行
- 格式:HTTP/1.1 200 ok
- 设置状态码:setStatus(int sc) 2) 设置响应头:setHeader(String name, String value) 3) 设置响应体: * 使用步骤:
- 获取输出流
- 字符输出流:PrintWriter getWriter()
- 字节输出流:ServletOutputStream getOutputStream()
- 使用输出流,将数据输出到客户端浏览器
response.getWriter().println(“xxxx”);
4)设置编码
response.setContentType(“text/html;charset=utf-8”);
package com.tledu.zrz.controller;
import java.io.IOException;
import java.io.PrintWriter;
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("/api1")
public class _01_ResponseAPI_01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 单独设置编码,需要在获取流之前,默认编码为ISO-8859-1
resp.setCharacterEncoding("utf-8");
// 响应类型和编码
resp.setContentType("text/html;charset=utf-8");
// 获取字符流,输出数据
PrintWriter pw = resp.getWriter();
pw.write("你好");
pw.println("我很好");
}
}
package com.tledu.zrz.controller;
import java.io.IOException;
import java.io.PrintWriter;
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("/api2")
public class _02_ResponseAPI_02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 单独设置编码,需要在获取流之前,默认编码为ISO-8859-1
resp.setCharacterEncoding("utf-8");
// 响应类型和编码
resp.setContentType("text/html;charset=utf-8");
// 字节
ServletOutputStream sos = resp.getOutputStream();
sos.write(97);
// 设置状态
// resp.setStatus(500);
}
}
package com.tledu.zrz.controller;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class _03_ServletContext extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
}
package com.tledu.zrz.controller;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class _04_init implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
// 局部参数获取,使用config对象调用getInitParameter(key) 获取
String username = config.getInitParameter("username");
// 全局参数需要使用servletContext获取,而servletContext被封装在servletConfig中
ServletContext servletContext = config.getServletContext();
String age = servletContext.getInitParameter("age");
System.out.println(username +" : "+age);
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
request ,session ,servletContext 区别
3. Ajax交互
package com.tledu.zrz.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/getData")
public class AjaxTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
// 响应类型
resp.setContentType("application/json;charset=utf-8");
// 1 数据库查询 获取所有用户 得到集合
// 2 把集合转换为jsons数组格式的字符串
// JSON数组格式的字符串,一般为数据库查询出来的,然后转换为JSON格式
String string = "[{id:1,'username':'admin','password':'root'},{id:2,'username':'test','password':'1234'}]";
// 响应数据
resp.getWriter().write(string);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
window.onload = function () {
var btn = document.getElementById("btn");
btn.onclick = function () {
/**
* 1 获取ajax引擎对象 XMLHttpRequest
* 2 配置请求连接的URL,代表浏览器要发出的请求以及目的地,还有就是是否异步
* 这个请求不是直接发送,而是配置给ajax引擎,有它去发送
* 3 监视XMLHttpRequest对象的请求流程,根据不同的请求结果,做出不同的响应
* 4 发送请求,上面都是准备,这里才是真正的发送
*/
// 1 获取ajax引擎对象 XMLHttpRequest
var xmlhttp;
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2 配置请求连接的URL
xmlhttp.open("GET", "getData", true);
// 3 监听请求状态
xmlhttp.onreadystatechange = function () {
// 为4 和 200 说明请求成功
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// 把JSON数组格式的字符串转换为JSON数组对象
var jsons = eval(xmlhttp.responseText);
// 遍历数组
for(var i = 0; i < jsons.length; i++){
// 遍历数组中每一个JSON对象
for(key in jsons[i]){
document.getElementById("saveData").innerHTML += jsons[i][key]+" ";
}
document.getElementById("saveData").innerHTML +="<br>";
}
}
}
// 4 发送请求
xmlhttp.send();
// post请求 如果需要传递参数 必须设置请求头
// xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// 参数 键=值&键=值
// xmlhttp.send("fname=Bill&lname=Gates");
}
}
</script>
</head>
<body>
<button id="btn">获取数据</button>
<div id="saveData"></div>
<input type="text" name="" id="">
</body>
</html>
JSON
package com.tledu.zrz;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.tledu.zrz.pojo.User;
public class Test {
public static void main(String[] args) {
// 1 数据库查询数据,得到集合
List<User> users = new ArrayList<User>();
users.add(new User(1, "admin", "root", "管理员"));
users.add(new User(2, "administrator", "root", "泽哥最帅"));
users.add(new User(3, "test", "1234", "2楼说的对"));
// 2 转换为JSON数组格式的字符串
System.out.println(JSON.toJSONString(users));
// 3 传递到页面
// resp.getWriter().write(JSON数组格式的字符串);
}
}
4. 初始化数据init
Init方法,最先执行,并且执行只执行一次,默认是第一次请求的时候执行
但是可以通过load-on-startup来设置执行时机
-1 是第一次请求
0和正整数 是启动tomcat的时候执行
结合init的执行时机,所以比较适合做一些数据初始化的工作
1 初始化java中对象或其他
2 初始化xml中的数据
1 局部
2 全局
可以解决硬代码问题,一般用来指定字符编码,和filter一起使用
4.1 局部
4.2 全局
5. 作用域
getAttribute
setAttribute
removeAttribute
以上三个方法,是request,session,servletContext都有的,用来向对应的作用域中保存数据,获取数据,删除数据
5.1 Request
当前请求有效,如果向request中保存数据,那么仅在当前请求中可以获取
一般用于页面和后端交互是需要进行的数据传递
package com.tledu.zrz.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/req")
public class _05_Request extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
// 创建输出流
PrintWriter pw = response.getWriter();
// 获取数据 ,返回Object类型,需要强制转换
Integer count = (Integer) request.getAttribute("count");
// 第一次请求 一定是获取不到的,因为得先保存,才能获取到
if(count == null){
count = 0;
}else{
count++;
}
// 保存数据
request.setAttribute("count", count);
// 向页面写出
pw.println("<h1 align='center'>"+count+"</h1>");
}
}
不管在那个浏览器,一次会话中只执行一次
一直都是0
5.2 Session
当前会话有效,建立的持续通信,一般是浏览器打开到浏览器关闭,是一次会话,不过也是可以设置会话时间的
一般用于保存登陆的用户信息
package com.tledu.zrz.controller;
import java.io.IOException;
import java.io.PrintWriter;
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 javax.servlet.http.HttpSession;
@WebServlet("/ses")
public class _06_Session extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
// 创建输出流
PrintWriter pw = response.getWriter();
// 获取session , true 说明没有session就新建一个
HttpSession session = request.getSession(true);
// 获取数据 ,返回Object类型,需要强制转换
Integer count = (Integer) session.getAttribute("count");
// 第一次请求 一定是获取不到的,因为得先保存,才能获取到
if(count == null){
count = 0;
}else{
count++;
}
// 保存数据
session.setAttribute("count", count);
// 向页面写出
pw.println("<h1 align='center'>"+count+"</h1>");
}
}
session 在服务器没有关闭 会话没有结束,刷新页面依然加一,同样打开新的链接,把url 赋值进去,数据不会丢失,因为,浏览器会有session,记住当前状态;如果换成其他浏览器,会话又是不一样的,是初始状态
会话中,数据共享
5.3 ServletContext
全局有效,只要服务器不关闭,永久存储,服务器关闭,数据消失
package com.tledu.zrz.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
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 javax.servlet.http.HttpSession;
@WebServlet("/sc")
public class _07_ServletContext extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
// 创建输出流
PrintWriter pw = response.getWriter();
// 获取servletContext
ServletContext application = request.getServletContext();
// 获取数据 ,返回Object类型,需要强制转换
Integer count = (Integer) application.getAttribute("count");
// 第一次请求 一定是获取不到的,因为得先保存,才能获取到
if(count == null){
count = 0;
}else{
count++;
}
// 保存数据
application.setAttribute("count", count);
// 向页面写出
pw.println("<h1 align='center'>"+count+"</h1>");
}
}
对比session ,换个浏览器试,还当前会话,不是新的会话
只要tomcat不重新启动,数据一直存在,共享
6. 重定向和请求转发
6.1 请求转发
- 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
- 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
forward
发送
package com.tledu.zrz.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/a/b/c")
public class _08_forward extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 通过servletContext对象中的getRequestDispatcher 进行跳转
// 因为这种是通过servletContext请求转发的,所以地址必须加 / ,/就是根目录(WebContent)
// request.getServletContext().getRequestDispatcher("/test.html")
// .forward(request, response);
// 直接通过request进行跳转
// 这种跳转,加 / 表示根目录(WebContent) , 不加 / 就是相对路径
// 比如 当前servlet的路由为 /a/b/c 则 url 是这样 : http://localhost:8080/Servlet_02/a/b/c
// 则 绝对路径 是 http://localhost:8080/Servlet_02/
// 相对路径是 http://localhost:8080/Servlet_02/a/b/
// /user/list
request.getRequestDispatcher("/test.html").forward(request, response);
// http://localhost:8080/Servlet_02/test.html
// http://localhost:8080/Servlet_02/a/b/test.html
}
}
特点:
1. 浏览器地址栏路径不发生变化
2. 只能转发到当前服务器内部资源中。
3. 转发是一次请求
客户无感知跳转,跳转方和被跳转方数据共享(request)
6.2 重定向
sendRedirect
直接发送
package com.tledu.zrz.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/send")
public class _09_sendRedirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 加/ 是绝对 : 绝对 定位到当前服务器 webapps,IP:8080
// 不加/ 是相对 : 同forward的相对
// 使用绝对要注意,需要写项目名
// resp.sendRedirect("/Servlet_02/test.html");
// 获取虚拟目录(/项目名)
String name = req.getContextPath();
resp.sendRedirect(name+"/test.html");
}
}
输入send 路由 会直接转到test.html
6.3 区别
forward 和 redirect 区别
Forward是服务器内部跳转,两个页面中数据互通,使用同一个request对象,但是地址栏中依然显示第一次请求的url地址,用户感觉不到页面进行了跳转,也只会发送一次请求。
sendRedirect是重定向,两个页面数据不互通,使用的不是同一个request对象,地址栏中显示的是第二个请求的url地址,会发送两次请求
重定向的特点:redirect
1. 地址栏发生变化
2. 重定向可以访问其他站点(服务器)的资源
3. 重定向是两次请求。不能使用request对象来共享数据
转发的特点:forward
1. 转发地址栏路径不变
2. 转发只能访问当前服务器下的资源
3. 转发是一次请求,可以使用request对象来共享数据
6.4 应用场景
1 比如登陆失败,或者被拦截到登陆页面,只要是需要用户输入之后,进行跳转的,一般使用重定向
如果此时使用forward跳转,则地址栏不变,那么此时如果刷新页面(重新请求地址栏),就会弹框,提示是否重复发送
Get请求 不会弹框提示,但是依然会发送
2 比如去后端获取数据,一般请求转发(因为request共享)
查询相关,都是forward
比如 搜索,用户列表
搜索比较特殊,需要传递数据(提交数据),一般搜索肯定使用get请求
如果是post请求,后端大部分使用重定向(一般发送数据使用post)
如果是get请求,后端大部分使用forward(一般获取数据使用get)
package com.tledu.zrz.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/login")
public class _10_LoginServlet 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"));
req.getRequestDispatcher("test.html").forward(req, resp);
}
}