Http和Servlet(request和response)
1.http
客户端和服务器端之间通信的规则。
版本
1.0 一次通信结束以后,就会断开连接
1.1 请求数据,一次通信结束后还会保持连接,除非关闭服务器端或者客户端。当然连接也有时限,如果一直空着,也会关闭。
http请求数据内容
请求行,请求头,请求体
* 请求行
POST /examples/servlets/servlet/RequestParamExample HTTP/1.1
POST : 请求方式 ,以post去提交数据
/examples/servlets/servlet/RequestParamExample
请求的地址路径 , 就是要访问哪个地方。
HTTP/1.1 协议版本
* 请求头
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: http://localhost:8080/examples/servlets/servlet/RequestParamExample
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 31
Connection: Keep-Alive
Cache-Control: no-cache
Accept: 客户端向服务器端表示,我能支持什么类型的数据。
Referer : 真正请求的地址路径,全路径
Accept-Language: 支持语言格式
User-Agent: 用户代理 向服务器表明,当前来访的客户端信息。
Content-Type: 提交的数据类型。经过urlencoding编码的form表单的数据
Accept-Encoding: gzip, deflate : 压缩算法 。
Host : 主机地址
Content-Length: 数据长度
Connection : Keep-Alive 保持连接
Cache-Control : 对缓存的操作
* 请求体
>浏览器真正发送给服务器的数据
发送的数据呈现的是key=value ,如果存在多个数据,那么使用 &
firstname=zhang&lastname=sansan
http响应数据内容
请求的数据里面包含三个部分内容 : 响应行 、 响应头 、响应体
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 673
Date: Fri, 17 Feb 2017 02:53:02 GMT
...这里还有很多数据...
* 响应行
HTTP/1.1 200 OK
协议版本
状态码
200 : 成功,正常处理,得到数据。
403 : for bidden 拒绝
404 : Not Found
500 : 服务器异常
OK
对应前面的状态码
* 响应头
Server: 服务器是哪一种类型。 Tomcat
Content-Type : 服务器返回给客户端你的内容类型
Content-Length : 返回的数据长度
Date : 通讯的日期,响应的时间
2.两种请求方式的区别
get
1.地址栏后面拼接数据,安全隐患。一般从服务器获取数据使用get方式。
2.能歇等待的数据太小,只有1kb。
post
1.以流的方式发送数据,一般向服务器发送数据使用该方式。
2.携带的数据大小没有上限。还有Content-Length来表明数据的长度。
3.Web资源的分类
静态资源
html,js,css
动态资源
Servlet,jsp
4.Servlet
Servlet就是一个运行在java上面的小程序,用于接受和响应客户端的http请求。
更多的时候是配合动态资源来做这件事。静态资源也会用到,用的是Tomcat里面已经定义好的的defaultServlet。
执行过程:
5.Servlet通用写法
Servlet (接口)
|
|
GenericServlet
|
|
HttpServlet (用于处理http的请求)
继承httpServlet或者实现Servlet接口。
主要实现的方法时doPost和doGet两种方法,一个处理get方式的请求,一个处理post方式的请求。
Service方法也能实现功能的原因是,他会判断发送过来的是什么方式,然后调用对应的doGet和doPost方法,完成对数据的处理。
6.Servlet的生命周期
* init方法
在创建该servlet的实例时,就执行该方法。
一个servlet只会初始化一次, init方法只会执行一次
默认情况下是 : 初次访问该servlet,才会创建实例。
* service方法
只要客户端来了一个请求,那么就执行这个方法了。
该方法可以被执行很多次。 一次请求,对应一次service方法的调用
* destroy方法
servlet销毁的时候,就会执行该方法
1. 该项目从tomcat的里面移除。
2. 正常关闭tomcat就会执行 shutdown.bat
> doGet 和 doPost不算生命周期方法,所谓的生命周期方法是指,从对象的创建到销毁一定会执行的方法, 但是这两个方法,不一定会执行。
让Servlet创建实例的时机 提前。
1. 默认情况下,只有在初次访问servlet的时候,才会执行init方法。 有的时候,我们可能需要在这个方法里面执行一些初始化工作,甚至是做一些比较耗时的逻辑。
2. 那么这个时候,初次访问,可能会在init方法中逗留太久的时间。 那么有没有方法可以让这个初始化的时机提前一点。
3. 在配置的时候, 使用load-on-startup元素来指定, 给定的数字越小,启动的时机就越早。 一般不写负数, 从2开始即可。
<servlet>
<servlet-name>HelloServlet04</servlet-name>
<servlet-class>com.itheima.servlet.HelloServlet04</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
7.ServletConfig
//1. 得到servlet配置对象 专门用于在配置servlet的信息
ServletConfig config = getServletConfig();
//获取到的是配置servlet里面servlet-name 的文本内容
String servletName = config.getServletName();
System.out.println("servletName="+servletName);
//2、。 可以获取具体的某一个参数。
String address = config.getInitParameter("address");
System.out.println("address="+address);
//3.获取所有的参数名称
Enumeration<String> names = config.getInitParameterNames();
//遍历取出所有的参数名称
while (names.hasMoreElements()) {
String key = (String) names.nextElement();
String value = config.getInitParameter(key);
System.out.println("key==="+key + " value="+value);
}
作用
刚好这个servlet 里面需要一个数字或者叫做变量值。 但是这个值不能是固定了。 所以要求使用到这个servlet的公司,在注册servlet的时候,必须要在web.xml里面,声明init-params
8.Servlet的配置方式
Servlet配置方式
* 1. 全路径匹配
> 以 / 开始 /a /aa/bb
> localhost:8080/项目名称/aa/bb
* 2. 路径匹配 , 前半段匹配
> 以 / 开始 , 但是以 * 结束 /a/* /*
> * 其实是一个通配符,匹配任意文字
> localhost:8080/项目名称/aa/bb
* 3. 以扩展名匹配
> 写法: 没有/ 以 * 开始 *.扩展名 *.aa *.bb
前半段匹配防止输入小错误,拓展名匹配可以权限管理
9.ServletContext
每个Web工程都只有一个ServletContext,不管在项目的哪个Servlet下面,都可以获取到。
有什么作用?
1. 获取全局配置参数
web.xml中
<context-param>
<param-name>address</param-name>
<param-value>三和</param-value>
</context-param>
servlet中:
ServletContext context = getServletContext();
String value = context.getInitParameter("address");
2. 获取web工程中的资源
共有如下三种方式:
private void demo3() throws IOException {
ServletContext context = getServletContext();
InputStream is = this.getClass().getClassLoader().getResourceAsStream("../../file/config.properties");
Properties p = new Properties();
p.load(is);
String value = p.getProperty("address");
System.out.println(value);
}
private void method2() throws IOException {
ServletContext context = getServletContext();
InputStream is = context.getResourceAsStream("/file/config.properties");
Properties p = new Properties();
p.load(is);
String value = p.getProperty("address");
System.out.println(value);
}
private void method1() throws FileNotFoundException, IOException {
ServletContext context = getServletContext();
String realPath = context.getRealPath("/file/config.properties");
// System.out.println(realPath);
Properties p = new Properties();
FileInputStream is = new FileInputStream(realPath);
p.load(is);
String value = p.getProperty("address");
System.out.println(value);
}
第一种是直接调用getRealpath方法获取全路径,第二种和第三种的文件路径分析如下图:
3. 存取数据,servlet间共享数据 域对象
案例代码:用ServletContext记录登记的次数
Object attribute = getServletContext().getAttribute("count");
int totalcount = 0;
if(attribute!=null){
totalcount = (Integer) attribute;
}
System.out.println("已经登陆的次数为"+totalcount);
getServletContext().setAttribute("count", totalcount+1);
在另一个Servlet中
int count = (Integer) getServletContext().getAttribute("count");
response.getWriter().write("登陆了"+count+"次");
ServletContext 何时创建, 何时销毁?
服务器启动的时候,会为托管的每一个web应用程序,创建一个ServletContext对象
从服务器移除托管,或者是关闭服务器。
* ServletContext 的作用范围
> 只要在这个项目里面,都可以取。 只要同一个项目。 A项目 存, 在B项目取,是取不到的? ServletContext对象不同。
10.HttpServletRequest
这个对象封装了一切从客户端提交过来的数据。
// 获取请求头的信息
// Enumeration<String> headerNames = request.getHeaderNames();
// while (headerNames.hasMoreElements()) {
// String name = (String) headerNames.nextElement();
// String value = request.getHeader(name);
// System.out.println(name+":"+value);
// }
// 获取传入的信息
// String name = request.getParameter("name");
// String address = request.getParameter("address");
// System.out.println("name="+name+",address="+address);
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = (String) parameterNames.nextElement();
System.out.println(request.getParameter(name));
}
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> keySet = parameterMap.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
String value = parameterMap.get(key)[0];
String value2 = parameterMap.get(key)[1];
String value3= parameterMap.get(key)[2];
System.out.println(key+"=="+value+value2+value3);
}
map的value不确定有几个,所以是数组形式。
在获取中文数据的时候,可能出现乱码 ,针对两种不同的方式,解决方案也不一样
get方式
1.转码显示
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username="+username+"password="+password);
String username1 = new String(username.getBytes("ISO-8859-1"),"utf-8");
System.out.println("username="+username+"password="+password);
get因为数据跟在url后面,已经完成了对应的转码工作,所以只能在显示的时候将其转化为指定编码的String才可以正确显示。
2.在Tomcat里面修改配置文件
在server.xml 加上URIEncoding="utf-8"
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
Post方式
在接收数据最开始设置解码的格式: request.setCharacterEncoding("UTF-8");
这个说的是设置请求体里面的文字编码。 get方式,用这行,有用吗? ---> 没用
这行设置一定要写在getParameter之前。
11.HttpServletResponse
和request的作用相反,负责返回数据给客户端。
response.setHeader("Location", "login_success.html");
response.setStatus(202);
可以设置响应头等信息。
当返回数据的时候,可能也会出现中文乱码的情况,解决方法:
response.setContentType("text/html;charset=UTF-8");
按照不同的方式,具体的细节不同,了解
以字符流输出: response.getWriter()
//1. 指定输出到客户端的时候,这些文字使用UTF-8编码
response.setCharacterEncoding("UTF-8");
//2. 直接规定浏览器看这份数据的时候,使用什么编码来看。
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.getWriter().write("我爱黑马训练营...");
* 以字节流输出 : response.getOutputStream()
//1. 指定浏览器看这份数据使用的码表
response.setHeader("Content-Type", "text/html;charset=UTF-8");
//2. 指定输出的中文用的码表
response.getOutputStream().write("我爱深圳黑马训练营..".getBytes("UTF-8"));
默认是utf-8,所以一般不用写。
12.点击下载资源手动编码的Web实现
不使用任何代码,超链接也可以完成下载
<h3>文件查看</h3>
<a href="file/family.jpg">啥家庭啊.jpg</a>
<br>
<a href="file/****.png">****.png</a>
<br>
原因是Tomcat里面有默认的DefaultServlet专门用来处理这些静态资源
手动编码实现下载:
<h3>文件下载</h3>
<a href="DownloadServlet?filename=family.jpg">啥家庭啊.jpg</a>
<br>
<a href="DownloadServlet?filename=****.png">****.png</a>
<br>
在静态资源中,超链接到Servlet的同时,携带参数:filename
Servlet中的代码实现:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 通过request获取到携带的参数
String filename = request.getParameter("filename");
// 资源存放位置+参数拼接路径名,并通过context获取到资源文件存放的真实路径
String path = getServletContext().getRealPath("/file/" + filename);
// 设置弹框提示下载请求
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
// 标准的输入输出流拷贝文件。从原文件输入,使用response作为输出
FileInputStream fis = new FileInputStream(path);
OutputStream os = response.getOutputStream();
int len = 0;
byte[] arr = new byte[1024 * 8];
while ((len = fis.read(arr)) != -1) {
os.write(arr, 0, len);
}
fis.close();
os.close();
}
如果要对中文名称的文件进行下载
// 因为是使用get方式,所以如果是中文,转码
filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
// 资源存放位置+参数拼接路径名,并通过context获取到资源文件存放的真实路径
String path = getServletContext().getRealPath("/file/" + filename);
// 判断浏览器的类型,给出对应的解决方式
String clientType = request.getHeader("User-Agent");
if (clientType.contains("Firefox")) {
filename = FireFoxEncoding.base64EncodeFileName(filename);
} else {
filename = URLEncoder.encode(filename, "UTF-8");
}
// 设置弹框提示下载请求
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
工具类对火狐浏览器请求处理如下
public static String base64EncodeFileName(String fileName) {
BASE64Encoder base64Encoder = new BASE64Encoder();
try {
return "=?UTF-8?B?"
+ new String(base64Encoder.encode(fileName
.getBytes("UTF-8"))) + "?=";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
上半部分针对请求进行转码,确保可以找到资源。下半部分确保下载栏显示的文件名称正确
13.请求转发和重定向
重定向
/*
之前的写法
response.setStatus(302);
response.setHeader("Location", "login_success.html");*/
//重定向写法: 重新定位方向 参数即跳转的位置
response.sendRedirect("login_success.html");
1. 地址上显示的是最后的那个资源的路径地址
2. 请求次数最少有两次, 服务器在第一次请求后,会返回302 以及一个地址, 浏览器在根据这个地址,执行第二次访问。
3. 可以跳转到任意路径。 不是自己的工程也可以跳。
4. 效率稍微低一点, 执行两次请求。
5. 后续的请求,没法使用上一次的request存储的数据,或者 没法使用上一次的request对象,因为这是两次不同的请求。
请求转发
//请求转发的写法: 参数即跳转的位置
request.getRequestDispatcher("login_success.html").forward(request, response);
1. 地址上显示的是请求servlet的地址。 返回200 ok
2. 请求次数只有一次, 因为是服务器内部帮客户端执行了后续的工作。
3. 只能跳转自己项目的资源路径 。
4. 效率上稍微高一点,因为只执行一次请求。
5. 可以使用上一次的request对象。
最根本的区别,一个request调用,一个response调用。一个处理前转页面,一个处理后转页面。