Servlet学习(2)-HTTP和Request

本文详细介绍了Servlet中关于HTTP请求的相关知识,包括HTTP协议的特点、请求数据格式、请求行、请求头、Request对象的功能,如获取请求行数据、请求头数据、请求参数等。此外,还提到了请求转发、共享数据以及解决中文乱码问题的方法,并建议通过实际案例加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



说明

  因为是个人复习java的总结,所以结构稍显杂乱,有些语句过于口语化.
  下面是部分的servlet内容,主要是Request对象的内容,后面还有很多内容没写.


HTTP

  其实就是客户端与服务器之间进行超文本传输的协议

  其特点

  1. HTTP是基于TCP/IP协议的高级协议,那么HTTP就肯定是安全的连接
  2. HTTP协议的默认端口号为80
  3. HTTP是基于请求/响应模型,也就是用户的一次请求对应,服务器的一次响应,所以一次连接只会处理一个响应.
  4. 是无状态的协议,也就是说每次请求都是独立的一次请求.服务不会对请求进行记录,不会因为之前请求缺失了什么数据而在这次连接补充,只会传递当前请求需要的数据.

  在HTTP1.0的时候对于请求每次都需要建立连接,但是1.1版本可以保持连接,看是不是有连续的请求,不会因为每次请求而建立连接.


请求的数据格式

  其实主要是为了更好地使用上面提到地Servlet地service()方法中传入的ServletRequest类.

  1. 请求行
GET /demo HTTP/1.1

  请求方式 请求URL版本信息

  1. 请求头
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

  请求头名称 : 请求头值
  其实就相当于请求格式编码之类的设置在这部分,都是通过请求头:请求头值的格式传递.

  1. 请求空行
    就是一行空行,用来分割请求头和请求体的
  2. 请求体
    如果是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中.

  接下来进行一些分析,虽然我已经写完了,但是具体代码就不贴了,旨在帮助大家理清思路,也是对自己思路的完善.

  1. 首先我们需要定义一个用户的实体类User来帮助需要进行的登陆操作,虽然单对于这个案例来说没什么必要,但是之后数据增加,那么一个实体类是很有必要的.那么这个类肯定是私有的账号,密码,编号等信息.之后有需求还可以加权限等级.
  2. …本来还想着理清思路,起点写得就有点不太对,想想还是不改,为之后回看反思.其实应该先要创建一个数据库表User,在表中定义需要的字段信息,然后再定义实体类User.
  3. 接下来就需要创建一个Dao包作为一些操作的封装,然后创建LoginDao作为用户登陆的类.但是创建之后当然不能直接写连接数据库,需要进行封装.
  4. 那么就需要创建一个utils包JDBCUtils类作为JDBC操作的封装.既然封装JDBC就再考虑使用Druid数据库连接池.那么就需要导入Druid包并创建properties配置文件.那么再JDBCUtils中就需要定义私有的DataSource,然后在静态代码块中进行DataSource的初始化操作
  5. 那么获取DataSource对象就需要先通过ClassLoader获取配置文件并转换成流,加载到Properties对象中,最后通过Druid中的工厂类创建DataSource.另外注意类中获取DataSource的方法需要定义,而且肯定是静态的,Utils没什么好创建对象的.
  6. 接下来就可以写LoginDao中的内容了,但是既然用了Druid,那就再考虑用一下JdbcTemplate,那就需要导入Spring下Template相关的包.然后就可以定义查询sql语句,并使用jdbcTemplate封装的查询方法快速地进行预编译地查询数据,并且返回User类型对象.
  7. 写完这些就需要进行一下单元测试,这也是为了以后减少开发的成本.那就创建Test类,使用Junit进行单元测试.
  8. 测试完就可以创建Servlet了,创建LoginServlet类并继承HttpServlet,然后重写doPost()和doGet().那可以考虑将doGet()的返回设置为调用doPost(),简化代码.
  9. 然后就使用request的getParameter()获取相应的请求数据,也就是用户名和密码,创建User对象,再调用LoginDao的登陆方法,将数据传入.
  10. 根据是否返回对象判断登陆成功与否.失败则使用getRequestDispatcher()转送Servlet,注意通过forward()传递request和response对象.然后在失败页面输出提示.而转送成功Servlet前先通过setAttribute()来设置传递的共享数据User,然后再提示页面获取一些信息来提示用户,如用户名.

  这样一个案例的流程就结束了,又写了一遍思路更加清楚了一些,但是写到最后才发现,因为我已经有之前写的前端例子,就根本没考虑前端.大家先做前端界面,也没必要做好看,随便做个form,做前端太烦了.

  补充,其实上面涉及到一步可以进一步封装,也就是在Servlet中对于request中数据封装到User对象的步骤.这个案例数据少所以自己装一下没什么事,原本是想写个类对这个步骤进行封装,然后发现有BeanUtils可以帮助进行封装,只需要通过上面提到的getParameterMap()获取全部数据再创建一个User对象传给BeanUtils就可以自动封装.那这样就简化了很多.



如有错误欢迎读者批评指正!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值