文章目录
说明
因为是个人复习java的总结,所以结构稍显杂乱,有些语句过于口语化.
下面是部分的servlet内容,主要是Request对象的内容,后面还有很多内容没写.
HTTP
其实就是客户端与服务器之间进行超文本传输的协议
其特点
- HTTP是基于TCP/IP协议的高级协议,那么HTTP就肯定是安全的连接
- HTTP协议的默认端口号为80
- HTTP是基于请求/响应模型,也就是用户的一次请求对应,服务器的一次响应,所以一次连接只会处理一个响应.
- 是无状态的协议,也就是说每次请求都是独立的一次请求.服务不会对请求进行记录,不会因为之前请求缺失了什么数据而在这次连接补充,只会传递当前请求需要的数据.
在HTTP1.0的时候对于请求每次都需要建立连接,但是1.1版本可以保持连接,看是不是有连续的请求,不会因为每次请求而建立连接.
请求的数据格式
其实主要是为了更好地使用上面提到地Servlet地service()方法中传入的ServletRequest类.
- 请求行
GET /demo HTTP/1.1
请求方式 请求URL版本信息
- 请求头
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
请求头名称 : 请求头值
其实就相当于请求格式编码之类的设置在这部分,都是通过请求头:请求头值的格式传递.
- 请求空行
就是一行空行,用来分割请求头和请求体的 - 请求体
如果是GET其实是没有请求体的,因为数据会加在请求URL的后面,而POST是有这部分的,也就是POST方式传递的数据会放在这部分.
username=aaa
其中的数据也是通过这种名称和值的方式传递的.
请求整体上是这样一个结构,自上而下组成一个请求消息.深入分析各个部分.
请求行
请求方式其实HTTP有大概9种方式:
1.0中
post
主要就是post和get,post是将数据放在请求体中进行提交,数据大小限制就相对较小
get
get是将数据放在URL后面进行提交,所以有数据的限制,太大就提交不了.其实安全方面和post差不多,都可以通过浏览器解析的内容爬取到,只是get更为明显,表现在URL上而已.
head
和get请求其实差不多,只是获取的是报头,也就是说只发送请求页面的的访问地址.
1.1中,先了解一下,以后再深入理解
trace
put
delete
options
patch
connect
常见的请求头
User-Agent
表示用户通过什么工具来访问服务器,使用的使用可以通过这个信息来进行判断,从而解决不同浏览器的兼容性问题.另外还可以用来做防爬虫,拒绝通过其他工具进行访问.
Accept
表明浏览器能够进行解析的格式,从上面的案例里其实也能看出来,里面有熟悉的test/html.
Accept-Encoding
字面可以理解就是编码格式
Accept-Language
能够接收的语言环境
Connection
其实表明的就是使用的是可以保持连接,还是不能保持连接也就是上面提到过的1.1开始可以保持连接等待多个资源的请求.
Referer
其实就是表示当前的浏览器是通过什么地方到达的访问,也就是会传入跳转当前页面之前的网页地址.获取这个信息就可以在服务器中进行一些防止盗链的操作,避免来自其他网站的访问.另外也可以进行一些流量的统计,可以统计来自其他网站的流量怎么样.
Cookie
关于Cookie的具体内容下面会继续,这里先了解一下有Cookie这个请求头
这里只是部分的请求头,其实还有很多,根据具体情况具体了解.
Request和Response
其实就是获取请求和做出回应的对象,在之前提到的servlet类实现中,有个service()方法,其中传入的参数就表现为servletRequest,servletResponse,其实就是Rquest和Response对象.
Request可以用来获取浏览器像服务器发送请求时传输的请求消息,也就是上卖弄HTTP说过的请求头,请求体等信息.
Response则是用来响应请求的
Request继承结构
ServletRequest接口是Servlet中对于Request的接口,有一个子接口HttpRequest.这时候就会想到之前提到过的Servlet接口直接实现和继承HttpServlet类.想起那个就很容易理解他们之间的继承关系.
但是再往下就没有实现类了,而创建Request的对象不需要我们实现,是在Tomcat中自动帮我们创建的,再Tomcat的源码中就会发现有一个实现类
org.apache.catalina.connector.RequestFacede.这个类中是对于Request的实现,会在请求的时候自动创建对象提供给我们使用.
URI和URL以及URN
URL和URN其实是包含在URI中的.
URI表示的是统一资源标志符,也就是能够唯一标识某种资源或者资源域的标识.
URL表示通过路径来唯一标识某个资源的方式.现在最流行URL以至于URI和URL几乎等同,因为在计算机中使用URL这种路径方式来唯一的标识资源比较符合我们使用的逻辑和习惯.不像URN使用唯一的名称来标识资源.还需要去遍历寻找对应的资源,那效率太低了.
URN就是通过统一的名称来标识某个资源.
总的来说就是URL是使用最广的,用路径层次结构来确定资源位置,而URN表示通过固定的名称来标识资源.URI则只要能够确定一种资源,不论方式,所以其中包含URL和URN.
Request的功能
获取请求行数据
String getContextPath()
获取虚拟目录的路径,指的是部署资源时的路径,不是servlet通过注解加的虚拟路径.比较有用,下面深入了解Request之后才能了解其作用.
String getServletPath()
获取Servlet的虚拟路径,指的是通过注解或者xml配置的虚拟路径
String getRequestURI()
获取URI,
StringBuffer getRequestURL()
获取URL
String getProtocol()
获取协议版本信息
String getRemoteAddr()
获取客户机的IP地址
String getMethod()
获取请求方式,也就是上面说的HTTP9种请求方式,名称可以和from种的method一起记,没什么用,HttpServlet已经帮你封装好了.
String getQueryString()
获取get请求方式下传递的数据,也就是在URL后面的,但是之后会有更好的方式,不大用
获取请求头数据
String getHeader(String name)
通过请求头的名称获取其中的值,因为上面提到过请求头是以类似键值的方式传输的.比较常用的一个方法
Enumeration<String> getHeaderName()
获取所有的请求头.其返回值的Enumeration类其实相当于一个枚举类,也就是迭代器的前身.可以用来遍历存储的元素,只是没有删除之类迭代器补充的功能,只有向下遍历.但是一般不使用这个方法,因为对我们来说需要使用的头直接去选择,遍历全部没有什么意义.
获取请求体数据
BufferedReader getReader()
获取字符输入流
ServletInputStream getInputStream()
获取字节输出流
获取请求体数据需要获取流对象,然后通过流对象进行数据的读取.
获取请求参数
String getParameter(String name)
就是通过参数名来获取参数值,是对于上面获取请求数据的改进方法,更为方便.
String[] getParameterValues(String name)
其实就是为了一些复选框,设置的名称相同时,选取之后请求头中的同个参数名就会有多个值,就可以使用这个方法获取其中的参数形成数组.
Enumeration<String> getParameterNames()
获取所有请求的参数名称
Map<String,String[]> getParameterMap()
获取所有的参数形成一个map.
注意这里的方法对于post和get是通用的,上面提到的方法是不通用的,所以有针对请求体的获取方法.这里的方法可以将doPost()和doGet()简化成一个,但是需要注意一下具体的使用情况.
请求转发
其实就是在服务器中,我们不可能将所有的功能都写在一个Servlet中,基于后期维护,肯定需要细化封装,那就牵扯到不同Servlet之间的跳转,这就是请求的转发.将request转到其他的Servlet中.
RequestDispatchar getRequestDispatcher(String path)
forward(SeverletRequest request, ServletResponse response)
通过上面的方法就可以获取转发器的对象,然后再使用这个对象使用forward()转发对象.上面两个方法一般会组合起来链式的书写,转发器这个对象肯定只用一遍,再赋值变量太浪费了.
需要注意一下请求转发的特点
地址栏不会发生改变,其实很好理解,转发器是再Servlet内部的转发,所以不需要浏览器进行请求.地址栏不会改变.
只能访问当前服务器内部署的Servlet,这也很好理解.
浏览器只有一次请求,这也很好理解.
其实forward()会和之后的sendRedirect()也就是重定向做比较,下面会具体了解.
共享数据
其实就是对于上面的转发而言,肯定会涉及到Servlet之间数据共享的问题.对于这个共享数据,有一个Request域的概念.其实就是对于一次请求中调用到的Servlet范围.在这个范围内,进行数据共享.
说白了就是一次请求一个相同的Request对象,转发之后还是这个对象,那获取数据,其实数据基本就是Request中的请求数据.那使用同一个Request不是肯定获取的肯定是共享的数据.
所以从概念上理解,资源共享一般是发生在一次请求中发生多次的Servlet的时候.需要共享的数据则是放在Request域中
void setAttribute(String name, Object obj)
Object getAttribute(String name)
void removeAttribute(String name);
上面三个方法其实就是在Request域中根据键值对的模式放入数据,读取数据和删除数据.
获取ServletContext
getServletContext()
可以获取ServletContext,先了解一下,后面会继续深入学习.
解决中文乱码问题
get中的中文,Tomcat8自己解决了这个问题
post则需要在读取request的流的时候,先设置其编码格式,然后就能正常地读取.因为之前学过一些jsp,这个方式就和jsp中的解决方式一样.
request.setCharacterEncoding("UTF-8");
巩固案例
其实即使敲完上面这些印象也不深刻,所以建议跟我一样做一个小的案例使用一下,顺便再复习一下前面的内容.
比如说做一个登陆的案例,需求就是有前端界面输入账号密码,然后在根据账号密码的正确性转到对应的输出Servlet中.
接下来进行一些分析,虽然我已经写完了,但是具体代码就不贴了,旨在帮助大家理清思路,也是对自己思路的完善.
- 首先我们需要定义一个用户的实体类User来帮助需要进行的登陆操作,虽然单对于这个案例来说没什么必要,但是之后数据增加,那么一个实体类是很有必要的.那么这个类肯定是私有的账号,密码,编号等信息.之后有需求还可以加权限等级.
- …本来还想着理清思路,起点写得就有点不太对,想想还是不改,为之后回看反思.其实应该先要创建一个数据库表User,在表中定义需要的字段信息,然后再定义实体类User.
- 接下来就需要创建一个Dao包作为一些操作的封装,然后创建LoginDao作为用户登陆的类.但是创建之后当然不能直接写连接数据库,需要进行封装.
- 那么就需要创建一个utils包JDBCUtils类作为JDBC操作的封装.既然封装JDBC就再考虑使用Druid数据库连接池.那么就需要导入Druid包并创建properties配置文件.那么再JDBCUtils中就需要定义私有的DataSource,然后在静态代码块中进行DataSource的初始化操作
- 那么获取DataSource对象就需要先通过ClassLoader获取配置文件并转换成流,加载到Properties对象中,最后通过Druid中的工厂类创建DataSource.另外注意类中获取DataSource的方法需要定义,而且肯定是静态的,Utils没什么好创建对象的.
- 接下来就可以写LoginDao中的内容了,但是既然用了Druid,那就再考虑用一下JdbcTemplate,那就需要导入Spring下Template相关的包.然后就可以定义查询sql语句,并使用jdbcTemplate封装的查询方法快速地进行预编译地查询数据,并且返回User类型对象.
- 写完这些就需要进行一下单元测试,这也是为了以后减少开发的成本.那就创建Test类,使用Junit进行单元测试.
- 测试完就可以创建Servlet了,创建LoginServlet类并继承HttpServlet,然后重写doPost()和doGet().那可以考虑将doGet()的返回设置为调用doPost(),简化代码.
- 然后就使用request的getParameter()获取相应的请求数据,也就是用户名和密码,创建User对象,再调用LoginDao的登陆方法,将数据传入.
- 根据是否返回对象判断登陆成功与否.失败则使用getRequestDispatcher()转送Servlet,注意通过forward()传递request和response对象.然后在失败页面输出提示.而转送成功Servlet前先通过setAttribute()来设置传递的共享数据User,然后再提示页面获取一些信息来提示用户,如用户名.
这样一个案例的流程就结束了,又写了一遍思路更加清楚了一些,但是写到最后才发现,因为我已经有之前写的前端例子,就根本没考虑前端.大家先做前端界面,也没必要做好看,随便做个form,做前端太烦了.
补充,其实上面涉及到一步可以进一步封装,也就是在Servlet中对于request中数据封装到User对象的步骤.这个案例数据少所以自己装一下没什么事,原本是想写个类对这个步骤进行封装,然后发现有BeanUtils可以帮助进行封装,只需要通过上面提到的getParameterMap()获取全部数据再创建一个User对象传给BeanUtils就可以自动封装.那这样就简化了很多.
如有错误欢迎读者批评指正!!