1 HTTP:
* 概念:Hyper Text Transfer Protocol 超文本传输协议
* 传输协议:定义了,客户端和服务器端通信时,发送数据的格式
* 特点:
1. 基于TCP/IP的高级协议
2. 默认端口号:80
3. 基于请求/响应模型的:一次请求对应一次响应
4. 无状态的:每次请求之间相互独立,不能交互数据
* 历史版本:
* 1.0:每一次请求响应都会建立新的连接
* 1.1:复用连接
依次为:1 请求行、2 请求头、3 空行、4 请求体
POST /login.html HTTP/1.1 # 请求行:请求方法、请求URL、HTTP协议
Host: localhost # 请求头
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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
Referer: http://localhost/login.html
Connection: keep-alive代表可以复用1.1版本的
Upgrade-Insecure-Requests: 1
# 空行
username=zhangsan #请求体
请求消息数据格式:
- ①请求行:包括
请求方式 请求url 请求协议/版本
,如GET /login.html HTTP/1.1
- HTTP协议有7中请求方式,常见GET和POST。。。
- Get:
- 请求参数在请求行中,在url后。
- 请求的url长度有限制的
- 不太安全
- Post:
- 请求参数在请求体中
- 请求的url长度没有限制的
- 相对安全
- ②请求头:客户端浏览器告诉服务器一些信息。是一些键值对。
- 常见的请求头:
- HOST:指出请求的目的地
- User-Agent:浏览器告诉服务器,客户端使用的浏览器版本信息,以解决浏览器的兼容性问题
- Referer:http://localhost/login.html,告诉服务器,(当前请求)从哪里来?防盗链、统计工作
- ③空行:用于分割POST请求的请求头,和请求体的。
- ④请求体(正文):封装POST请求消息的请求参数的
状态码:
- 1xx:指示信息–表示请求已接收,继续处理
- 2xx:成功–表示请求已被成功接收、理解、接受
- 3xx:重定向–要完成请求必须进行更进一步的操作
- 4xx:客户端错误–请求有语法错误或请求无法实现
- 5xx:服务器端错误–服务器未能实现合法的请求
HTTP请求方法
HTTP工作原理:
HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
1、客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.oakcms.cn。
2、发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
3、服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
4、释放连接TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
5、客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
2 请求数据组成:
get的-------
post的-------
上面的请求的组成,下面介绍请求响应的组成
请求响应
- ①状态行
- ②响应头
- ③空行
- ④响应正文(html)
3 Request请求处理
3.1、request对象和response对象的原理
1. request和response对象是由Tomcat服务器创建的,【每次请求都会新建】。作为实参传递给service方法里。
不管是来自get的还是来自post的,都会在request对象中。每一次发送请求,请求的详细信息会被Tomcat自动封装成一个request对象,我们以后要截取当前请求的一些信息,我们使用request对象即可。
2. request对象是来获取请求消息,response对象是来设置响应消息
作用:request对象中封存了当前请求的所有请求信息
3.2、request对象继承体系结构:
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口(常用)
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)
Servlet的使用流程:
- 设置请求编码格式
- 设置响应编码格式
- 获取请求信息
- 处理请求信息
- 响应处理结果
3.3 获取请求行数据
比如以GET方式传输来的URL为:`/day14/demo1?name=zhangsan`
在service()中可以调用下面的方法。
1. 获取请求方式 ://返回GET或Post
String getMethod() // request.getMethod();
2. (*)获取虚拟目录:/day14 以/开始,不以/结束
String getContextPath()
3. 获取Servlet路径: /demo1
String getServletPath()
4. 获取get方式请求参数:name=zhangsan
String getQueryString()
5. (*)获取请求URI:/day14/demo1
String getRequestURI(): //虚拟项目和别名// /day14/demo1
StringBuffer getRequestURL() // :http://localhost/day14/demo1
URL:统一资源定位符 : http://localhost/day14/demo1 中华人民共和国
URI:统一资源标识符 : /day14/demo1 共和国
6. 获取协议及版本:HTTP/1.1
req.getScheme();//获取协议//一般应用返回的是http、SSL返回的是https;
String getProtocol();
request.getServerName();//返回当前页面所在的服务器的名字,IP
request.getServerPort();//返回当前页面所在的服务器使用的端口,80
String path = request.getContextPath();//项目名
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";//http://localhost:8080/项目名/
7. 获取客户机的IP地址:
String getRemoteAddr()
获取请求行数据
req.getHeader("键名");//返回指定的请求头信息
req.getHeaderNames();//返回请求头的键名的枚举集合
ServletContext application = req.getServletContext();
3.4 获取请求头数据
(不论get还是post请求方式都可以使用下列方法来获取请求参数)
1. 通过请求头的名称获取请求头的值
String req.getHeader(String name键名)//找不到返回null,不报错
2. 获取所有的请求头名称
Enumeration e = request.getHeaderNames()//获取所有的请求头名称
while(e.hasMoreElements()){
String name = (String)e.nextElenment();//获取键
String value = req.getHeader(name);//获取值
System.out.println( name+":"+value);
}
3.5 获取请求体数据
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
* 步骤:
1. 获取流对象
* BufferedReader getReader():获取字符输入流,只能操作字符数据
* ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
* 在文件上传知识点后讲解
2. 再从流对象中拿数据。
获取用户数据
- String req.getParameter("键名");//返回指定的用户数据,一对多时可能漏值。好像get和post都能使用
- String[] req.getParameterValues("键名");//返回同键不同值的请求数据(多选),返回的数组。
- Enumeration<String> req.getParameterNames()//返回所有用户请求数据的枚举集合
- Map<String,String[]> getParameterMap()//获取所有参数的map集合
注意: 如果要获取的请求数据不存在,不会报错,返回null。
注意:
request对象由tomcat服务器创建,并作为实参传递给处理请求的servlet的service方法。
3.6 获取ServletContext:
ServletContext getServletContext()
4 获取数据时中文乱码问题
乱码问题分为:
- 请求乱码:浏览器发给服务器的数据,服务器收到解析出现乱码。我们应该告诉服务器我们使用的请求编码格式
- 响应乱码:服务器发给浏览器的数据,浏览器收到解析出现乱码
4.1 请求编码:POST方式
乱码原因:
- 浏览器默认的数据编码格式是GBK,浏览器发的是GBK,(不一定,拿Fiddler软件看吧)
- tomcat服务器解析是用给定utf-8,会乱码 。
说明:对于POST方式,它采用的编码是由页面来决定的,如ContentType(“text/html; charset=GBK”)。当通过点击页面的submit按钮来提交表单时,浏览器首先会根据ContentType的charset编码格式来对POST表单的参数进行编码然后提交给服务器,在服务器端同样也是用ContentType中设置的字符集来进行解码,这就是通过POST表单提交的参数一般而言都不会出现乱码问题。
当然这个字符集编码我们是可以自己设定的,通过:
解决方案:
方法1:在 service 方法中使用:
request.setCharacterEncoding(“utf-8”);//设置编码
request.getParameter("");//获取数据
方法2:(实际更常用)
springMVC已经提供了轮子给我们使用,在web.xml添加post乱码filter:
在web.xml中加入:
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置请求编码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 设置响应编码格式为也是encoding -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern><!-- 拦截所有用户请求 -->
</filter-mapping>
4.2 请求编码:GET方式
乱码原因
- get请求方式的参数是放到了URL后面。这时设置content-type是没用的。
- tomcat服务器收到这个请求就会根据设置的调用默认的URIEncoding来进行解码,默认为ISO8859-1,并封装成request对象。
Tomcat8及以上版本不存在乱码问题,请求URL默认的编码是UTF-8,但是请求体还是使用的ISO-8859-1
Tomcat7及之前的服务器使用的是ISO8859-1编码,不支持中文
解决方案:
方法一(String编码,不推荐):
new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8");
//原来以为的解析方式是8859的,所以我们先用8859解出来字节,再用utf8重新编码字符
方法二(tomcat/conf/server.xml设置):
解决方案是通知设置tomcat编码URL的格式与请求体一致,或单独设置URL编码格式
在 tomcat 服务器目录下的 conf 文件下找到 server.xml 文件,打开进行如下配置:
<Connector port="8080" protocol="HTTP/1.1" maxThreads="150"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="utf-8"/> (单独设置了URL编码格式)
---------------或 -------------------------
<Connector port="8080" protocol="HTTO/1.1"
connectionTimeout="20000"
redirection="8443"
useBodyEncodingForURI="true">(设置URL的编码格式与请求体一致)
然后再使用req.setCharacterEncoding("");//设置请求体编码格式
----------------------------------------
(默认是iso-8859-1)。
4.3 响应编码
设置响应编码格式:告诉浏览器接收的格式(response默认直接使用ISO-8859-1编码然后交给浏览器)
方式1:在使用response对象之前设置编码格式(不用)
response.setCharacterEncoding("UTF-8");//通知服务器用什么编码集将字符转为字节//只能让字符串不乱吗,浏览器还是不知道如何解析页面字符串
方式2:在使用response对象之前设置一个响应头,通知浏览器以什么编码打开,告诉response对象如何读取字符串
response.setContentType("text/html;charset=Utf-8");
//或resp.setHeader("content-type","text/html;charset=utf-8");//等价
//只要设置过ContentType,服务器自动识别编码会自动设置response.setCharacterEncoding("")
方式3:在web.xml中配置过滤器:稍后介绍
https://blog.csdn.net/qq_38409944/article/details/80637980
4.4 SpringMVC中的编码Filter
springMVC已经提供了轮子给我们使用,在web.xml添加post乱码filter:
在web.xml中加入:
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置请求编码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 设置响应编码格式为也是encoding -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern><!-- 拦截所有用户请求 -->
</filter-mapping>
服务器启动的时候就会创建Filter,将init-param中的参数加载,注入到CharacterEncodingFilter 类中,浏览器每次发送请求都会经过这个过滤器,然后调用doFilterInternal
可以看到CharacterEncodingFilter继承OncePerRequestFilter,OncePerRequestFilter是个抽象类,其中的抽象方法doFilterInternal被CharacterEncodingFilter实现,同时OncePerRequestFilter实现了doFilter方法,调用了doFilterInternal方法。
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {//条件为设置的encoding不为空 且 (传入了forceEncoding或CharacterEncoding为空)
request.setCharacterEncoding(this.encoding);//关键语句
if (this.forceEncoding) {// 关键语句
response.setCharacterEncoding(this.encoding);
}
}
filterChain.doFilter(request, response);
}
5 Response
在使用Request对象获取了请求数据并进行处理后,处理的结果如何显示到浏览器中呢?答案是:使用Response对象。
解释:服务器在调用指定的Servlet进行请求处理的时候,会给Servlet的方法传递两个实参request和response。其中resquest中封存了请求相关的请求数据,而response则是用来进行响应的一个对象。
使用:
- 设置响应头
- 设置响应编码格式
- 设置响应实体
响应设置
Respone对象:
作用: 用来响应数据到浏览器的一个对象
搜httpServletResponse
使用: 设置响应头
- setHeader(String name,String value);//在响应头中添加响应信息,但是同键会覆盖
- addHeader(String name,String value);//在响应头中添加响应信息,但是不会覆盖。
设置响应状态
- sendError(int num,String msg);//自定义响应状态码(响应错误信息)。响应码说明
例:response.sendError(404,"sorry返回的信息");
设置响应实体
- resp.getWriter().write(String str);//响应具体的数据给浏览器
//什么叫响应实体:即str的内容是我们html可以识别的内容,相当于把这个实体写入到我们的浏览器中,而这个实体是浏览器可以直接识别的如<h1>等内容
例:resp.getWriter().write("<br>");
6 请求转发
问题:
服务器在接收到浏览器的请求后 ,仅仅使用一个Servlet进行请求处理,会造成不同的 Servlet 逻辑代码冗余,Servlet 的职责不明确。
解决:
使用请求转发。
特点:
- 一次请求,浏览器地址栏信息不改变。多次转发
- 只能转发到当前服务器内部资源中。
- 转发是一次请求
共享数据:
-
域对象:一个有作用范围的对象,可以在范围内共享数据
-
request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
-
void setAttribute(String name,Object obj):存储数据 Object getAttitude(String name):通过键获取值 void removeAttribute(String name):通过键移除键值对 获取ServletContext: ServletContext getServletContext()
请求转发使用方法:
步骤1. 通过request对象获取【请求转发器对象】:RequestDispatcher getRequestDispatcher(String path)
例:RequestDispatcher rd = request.getRequestDispatcher("URL地址")
步骤2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
例:request.getRequestDispatcher("URL地址").forward(request, response)
有三种方式得到RequestDispatcher对象:
1.ServletRequest接口中的getRequestDispatcher
2.ServletContext接口中的getRequestDispatcher()和getNamedDispatcher
区别:
ServletContext中的参数必须以斜杆/开始,context root上下文根的绝对路径
ServletRequest中的参数可以写绝对路径,也可以写相对路径。
Request 对象作用域
问题:使用请求转发后,不同的 Servlet 之间怎么进行数据的共享呢?或者说数据怎么从一个 servlet 流转给另外一个 Servlet 呢?
解决:使用 request 对象的作用域
使用:
- 设置:
request.setAttribute(object name,Object value);
(session同理) - 获取:
request.getAttribute(Object obj)
作用:解决了一次请求内的不同 Servlet 的数据(请求数据+其他数据)共享问题。
作用域:基于请求转发,一次请求中的所有 Servlet 共享。
注意:使用 Request 对象进行数据流转,数据只在一次请求内有效。
特点:服务器创建,每次请求都会创建,生命周期一次请求
localhost:8080/login/page
localhost:8080/login/login?uname=张三&pwd=123
比如登录失败应该返回登录界面让重新登录,而不是只输出登录失败
-------------------
login Servlet中:
//使用request对象实现不同Servlet的数据流转//增加到request中
req.setAttribute("str", "用户名或密码错误");//处理意见
//使用请求转发
req.getRequestDispatcher("转到的servlet--page登录页面").forward(req, resp);
return;
----------------------
在pageServlet中添加
String str=(String) req.getAttribute("str");//返回的是Object,所以得转换
//获取request作用域数据
String str=(String) req.getAttribute("str")==null?"":(String) req.getAttribute("str");
resp.getWriter().write("<font color='red' size='20px'>"+str+"</font>");//str错误信息展示
---------------------------
注意转发了request和response,相当于获取了相同的请求数据。
response用于转发处理建议,相当于add response。
7 重定向
问题:
- 如果当前的请求,Servlet 无法进行处理怎么办?
- 如果使用请求转发,造成表单数据重复提交怎么办?
自己处理不了,告诉谁可以处理
解决:使用重定向
语法:
response.sendRedirect(“/url路径”) //重定向的uri,第一个/代表项目根目录
-
本地路径为:uri
-
网络路径为:定向资源的 URL 信息
特点:
- 地址栏改变
- 发生了两次请求,第二次会覆盖第一次的请求数据。
- 数据不共享:
sendRedirect
告诉服务器重新发一个新的请求去访问mainServlet,里面没有了账号密码。所以频繁刷新还是刷新的main。请求与请求间没有关系。浏览器地址栏信息改变,避免表单重复提交
时机:如果请求中有表单数据,而数据又比较重要,不能重复提交,建议使用重定向。如果请求被Servlet接收后,无法进行处理,建议使用重定向定位到可以处理的资源。
原来getRequestDispatcher方法地址栏还是localhost:8080/login/login?uname=张三&pwd=123
,暴露了登录数据,F5刷新会频繁地登录
但是此时丢失了数据
重定向是response的另外一个用处,与forward不同的是,重定向会丢失所有的请求参数和request范围的属性,因为重定向将生成第二次请求,与前一次请求不在同一个request范围内,所以发送一次请求的请求参数和request范围的属性全部丢失。
一、表单的重复提交:
①. 在表单提交到一个Servlet ,而Servlet 又通过请求转发的方式响应一个JSP(HTML) 页面, 这个时候地址栏显示的是Servlet的路径, 再刷新页面,这个时候会造成表单的重复提交。
②. 在响应页面没有到达时候重复点击“提交按钮”,会造成表单的重复提交。
③. 你在响应页面时候点击返回,在点击提交按钮的时候,会造成表单的重复提交。
二、如何避免表单的重复提交呢?
要防止表单重复提交,就要标识用户的每一次访问请求,使得每一次访问对服务端来说都是唯一确定的。为了标识用户的每次访问请求,可以在用户请求一个表单域时增加一个隐藏表单项,这个表单项的值每次都是唯一的token,如:
①. 在原表单生成一个随机值token 。
②. 在原表单把随机值token 放入session 和隐藏域(type="hidden")。
③. 在serlvet 中,获取session 跟隐藏域的值:
> 若一致,把session 中的Attribute 清除。
> 若不一致,直接重定向到提示页面,并且return 程序。
表单页面
<%@page import="org.apache.catalina.Session"%>
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
String token=new Date().getTime()+"";
HttpSession session= request.getSession(true);
session.setAttribute("token", token);
%>
<h1>Step-1:选择要购买的书籍</h1>
<form action="<%=request.getContextPath() %>/procesStep1" method="post">
<input type="hidden" name="token" value="<%=token%>">
<table cellpadding="10" cellspacing="0" border="1px">
<tr>
<td>书名</td>
<td>购买</td>
</tr>
<tr>
<td>java</td>
<td><input type="checkbox" name="book" value="java"></td>
</tr>
<tr>
<td>C++</td>
<td><input type="checkbox" name="book" value="c++"></td>
</tr>
<tr>
<td>Python</td>
<td><input type="checkbox" name="book" value="python"></td>
</tr>
<tr>
<td>web</td>
<td><input type="checkbox" name="book" value="web"></td>
</tr>
<tr>
<td colspan="2"> <input type="submit" value="submit"></td>
</tr>
</table>
</form>
</body>
</html>
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//1550936037932
String sessionToken = (String)request.getSession().getAttribute("token");
String requestToken = request.getParameter("token");
if(sessionToken!=null&&sessionToken.equals(requestToken)) {
request.getSession().removeAttribute("token");
}else {
//request.getRequestDispatcher("/shoppingCart/relogin.jsp");
response.sendRedirect(request.getContextPath()+"/shoppingCart/relogin.jsp");
return ;
}
//request.setCharacterEncoding("UTF-8");
ServletContext servletContext = request.getServletContext();
String servletContextName = servletContext.getServletContextName();
ServletConfig servletConfig = getServletConfig();
String[] parameterValues = request.getParameterValues("book");
request.getSession().setAttribute("book", parameterValues);
String contextPath = request.getContextPath();
System.out.println(contextPath);
//下面是绝对路径
// response.sendRedirect(contextPath+"/shoppingCart/step_2.jsp");
//相对路径
response.sendRedirect("shoppingCart/step_2.jsp");
}
请求转发VS重定向
一、请求转发和请求重定向的区别
转发forward | 重定向redirect | |
---|---|---|
请求 | 执行forward后依然是上一次请求 | 执行redirect生成第二次请求 |
请求参数 | forward的目标页面可以访问原请求的请求参数,因为依然是同一次请求,所有原请求的请求参数、request范围的属性全部存在 | redirect的目标页面不能访问原请求的请求参数,因为第二次请求了,所有原请求的请求参数、request范围的属性全部丢失 |
地址栏 | 地址栏里的请求URL不会改变 | 地址栏改为重定向的目标URL。相当于在浏览器地址栏里输入新的URL后按回车键 |
请求转发和请求重定向又分别称为服务器跳转和客户端跳转
1.1、组件之间可否共享信息不同(本质区别)
当客户端向服务器发送请求时,服务器收到请求后,会将请求封装成一个HttpServletRequest对象request,并且所有的请求参数都封装在request对象中,这个对象时Jsp的内置对象可以直接在Jsp中使用。服务器收到请求后,还需要请求别的页面,这是就有两种方式:请求转发和请求重定向。
-
请求转发:客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于一个每个信息资源是共享的。请求由服务器转发给另外一个页面处理,如何转发,何时转发,转发几次,客户端是不知道的。请求转发时,从发送第一次到最后一次请求的过程中,web容器只创建一次request和response对象,新的页面继续处理同一个请求。也可以理解为服务器将request对象在页面之间传递。
-
请求重定向:实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。是客户端的行为,每次请求重定向都是由客户端发起的,也就是说重定向一次,就刷新request对象的属性,之前的request对象的属性值就失效了。
1.2、浏览器地址栏显示不同(表面区别)
- 请求转发之后地址栏的信息并不会有任何的改变。
- 请求重定向之后地址栏是会改变的,变为跳转之后的页面地址。
二、java实现
2.1 java中的请求转发(服务器跳转)
执行到跳转语句时就会立刻进行跳转
request.getRequestDispatcher("success.jsp").forward(request,response);
在JSP中,可以使用
ServletContext context = getServletContext();
context.getRequestDispatcher("success.jsp").forward(request,response);
在jsp页面中你也会看到通过下面的方式实现转发:
<jsp:forward page="success.jsp" />
2.2 java中的请求重定向(客户端跳转)
-
response.sendRedirect(“apage.jsp”); 不能传递request 范围的属性 ,但是可以通过地址重写的方式向跳转页传递参数,因为该方法执行完之后就相当于一次http request 的结束,这是服务器会向客户端发送302 状态码和新的url ,告诉客户端重新发送request 请求到新的url ,然后客户端照此执行,执行即新的请求响应流程开始,服务器再重新创建HttpServletRequest 对象和HttpServletResponse 对象,此时两个请求已经不在一个线程了,所以request 和response 对象都不是开始的那个了;
-
response.setHeader(“refresh”,“2;URL=index.jsp”) 2 秒后跳转到其他页面;
-
<a href="http://www.baidu.com"> 百度</a> 超链接
-
表单提交
三、路径问题
转发和重定向的URLString前有加 / 为绝对路径 ,反之为相对路径
-
服务器解析的路径:请求转发
-
浏览器解析的路径:主要写在html中的路径都是浏览器解析,除此之外都是服务器解析。
3.1绝对路径
1、重定向的 / 表示:http://服务器ip:端口/
response.sendRedirect("/Manager/index.jsp")
生成的地址:web服务器本身地址+参数生成完整的URL 即:http://localhost:8080/Manager/index.jsp
2、请求转发的 / 表示:http://服务器ip:端口/项目名
。是在项目内转发,所以无需项目名
request.getRequestDispatcher("/index.jsp").forward(request, response);
生成的地址:http://localhost:8080/项目名/index.jsp
请求转发地址的写法和servlet配置的urlPatterns的语法规则一样,/代表的是当前项目
重定向是浏览器发来的,只知道发到某个服务器,但是不知道发到服务器的哪个project,所以需要自己用代码声明; 而请求转发是服务器某个project内部的转发,转来转去都是在某个project内部,所以不需要手动声明项目名。
3.2 相对路径
假设通过表单请求指定的Url资源 action=“LoginServlet”
则表单生成的请求地址为:http://localhost:8080/项目名/LoginServlet
1、请求重定向:response.sendRedirect(“Manager/index.jsp”)
生成相对路径:http://localhost:8080/项目名/Manager/index.jsp
2、请求转发:相对路径情况下生成的完整URL与重定向方法相同。
代码
-------------response.java---------
package com.bjsxt.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取请求信息...
//获取请求头...
//获取请求行...
//获取用户数据...
//处理请求...
//响应处理结果
//设置响应头
resp.setHeader("mouse", "two fly birds");
resp.setHeader("mouse", "bjsxt");
resp.addHeader("key", "thinkpad");
resp.addHeader("key", "wollo");
//设置响应编码格式
//resp.setHeader("content-type", "text/html;charset=utf-8");
//resp.setContentType("text/plain;charset=utf-8");//普通文本
//resp.setContentType("text/xml;charset=utf-8");
resp.setContentType("text/html;charset=utf-8");
//设置响应状态吗
//resp.sendError(404, "this Method is not supported");
//设置响应实体
resp.getWriter().write("<b>今天天气真好,适合学习</b>");
}
}
案例 :用户登录
* 用户登录案例需求:
1.编写login.html登录页面
use rname & password 两个输入框
点击登录完成登录操作
2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
数据库–登录页面page,
转到Servlet:login进行密码验证,New ServiceImple层对象,调用密码验证方法
转到Service(接口+imple),New DaoImple
转到Dao,进行JDBC
* 分析
* 开发步骤
1. 创建项目,导入html页面,配置文件,jar包
2. 创建数据库环境
CREATE DATABASE day14;
USE day14;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) UNIQUE NOT NULL,
PASSWORD VARCHAR(32) NOT NULL
);
INSERT INTO
3. 创建包cn.itcast.domain,创建类User
package cn.itcast.domain;
/**
* 用户的实体类
*/
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
4 创建包cn.itcast.util,编写工具类JDBCUtils
package cn.itcast.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
//JDBC工具类 使用Durid连接池
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接池对象
public static DataSource getDataSource(){
return ds;
}
//获取连接Connection对象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
5 创建包cn.itcast.dao,创建类UserDao,提供login方法
package cn.itcast.dao;
import cn.itcast.domain.User;
import cn.itcast.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 操作数据库中User表的类
*/
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登录方法
* @param loginUser 只有用户名和密码
* @return user包含用户全部数据,没有查询到,返回null
*/
public User login(User loginUser){
try {
//1.编写sql
String sql = "select * from user where username = ? and password = ?";
//2.调用query方法
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();//记录日志
return null;
}
}
}
6 编写cn.itcast.web.servlet.LoginServlet类
package cn.itcast.web.servlet;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
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.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding("utf-8");
//2.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.封装user对象
User loginUser = new User();
loginUser.setUsername(username); loginUser.setPassword(password);
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判断user
if(user == null){
//登录失败
req.getRequestDispatcher("/failServlet").forward(req,resp);
}else{
//登录成功
//存储数据
req.setAttribute("user",user);
//转发
req.getRequestDispatcher("/successServlet").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
7 编写FailServlet和SuccessServlet类
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取request域中共享的user对象
User user = (User) request.getAttribute("user");
if(user != null){
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录成功!"+user.getUsername()+",欢迎您");
}
}
}
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录失败,用户名或密码错误");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
8 login.html中form表单的action路径的写法
* 虚拟目录+Servlet的资源路径
9 BeanUtils工具类,简化数据封装
* 用于封装javaBean的
1. javaBean:标准的java类
1. 要求:
1. 类必须被public修饰
2. 必须提供空参的构造器
3. 成员变量必须使用private修饰
4. 提供公共setter和getter方法
2. 功能:封装数据
2. 概念:
成员变量:
属性:setter和getter方法截取后的产物
例如:getUsername() --> Username--> username
3. 方法:
1. setProperty()
2. getProperty()
3. populate(Object obj , Map map):将map集合的键值对信息,封装到对应的javaBean对象中
package com.bjsxt.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* request对象学习:
* 作用:request对象中封存了当前请求的所有请求信息
* 使用:
* 获取请求头数据
* req.getMethod();//获取请求方式
* req.getRequestURL();//获取请求URL信息
* req.getRequestURI();//获取请求URI信息
* req.getScheme();//获取协议
* 获取请求行数据
* req.getHeader("键名");//返回指定的请求头信息
* req.getHeaderNames();//返回请求头的键名的枚举集合
* 获取用户数据
* req.getParameter("键名");//返回指定的用户数据
* req.getParameterValues("键名");//返回同键不同值的请求数据(多选),返回的数组。
* req.getParameterNames()//返回所有用户请求数据的枚举集合
* 注意:
* 如果要获取的请求数据不存在,不会报错,返回null。
*
* 注意:
* request对象由tomcat服务器创建,并作为实参传递给处理请求的servlet的service方法。
*
*/
public class RequestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取请求头数据
//获取请求方式
String method = req.getMethod();
System.out.println(method);
//获取请求URL
StringBuffer url=req.getRequestURL();
System.out.println(url);
//获取URI
String uri=req.getRequestURI();
System.out.println(uri);
//获取协议
String h=req.getScheme();
System.out.println(h);
//获取请求行数据
//获取指定的请求行信息
String value=req.getHeader("aaa");
System.out.println(value);
//获取所有的请求行的键的枚举
Enumeration e = req.getHeaderNames();
while(e.hasMoreElements()){
String name=(String) e.nextElement();
String value2=req.getHeader(name);
System.out.println(name+":"+value2);
}
//获取用户数据
String name=req.getParameter("uname");
String pwd=req.getParameter("pwd");
System.out.println(name+":"+pwd);
//String fav=req.getParameter("fav");
String[] favs=req.getParameterValues("fav");
if(favs!=null){
for(String fav:favs){
System.out.println(fav);
}
}
//获取所有的用户请求数据的键的枚举集合---req.getParameterNames()
}
}