Fork me on GitHub

notes_for_HeadFirstServletsAndJsp4

Head First Servlets&JSP 读书笔记_4

HttpServletRequest接口扩展了ServletRequest接口,发生了什么?

与上同理的还有HttpServletResponse接口扩展了ServletResponse接口。以ServletRequest接口为例解释如下:

avax.servlet Interface ServletRequest
Defines an object to provide client request information to a servlet. The servlet container creates a ServletRequest object and passes it as an argument to the servlet’s service method.
[扩展译]当Servlet容器接收到客户端要求访问特定Servlet的请求时,容器先解析客户端的原始请求数据,把它包装成一个ServletRequest对象。当容器调用Servlet对象的service()方法时,就可以把ServletRequest对象呢作为参数传给service()方法。

A ServletRequest object provides data including parameter name and values, attributes, and an input stream. Interfaces that extend ServletRequest can provide additional protocol-specific data (for example, HTTP data is provided by HttpServletRequest.
[译]ServletRequest对象提供参数名、参数值、属性、输入流等数据。其扩展接口(比如HttpServletRequest会提供Http数据)会格外增加特定的协议数据。

Http协议数据主要有如下:

method description
getContextPath():String Returns the portion of the request URI that indicates the context of the request.[译]获得Request中传入的URI部分(注:URI标记了资源,未给出资源地址,故只是URL的一部分,参考:Web基础-Uri跟Url的区别
getCookies():Cookie[] Returns an array containing all of the Cookie objects the client sent with this request.[译]获得Request相关的cookie数组
getHeader(String name):String Returns the value of the specified request header as a String.[译]获得客户的平台和浏览信息
getSession():HttpSession Returns the current session associated with this request, or if the request does not have a session, creates one.[译]返回客户相关的Session会话
getInputStream():ServletInputStream Retrieves the body of the request as binary data using a ServletInputStream[译]返回请求体中的二进制内容(请求体若要处理计算机驱动,可能包含二进制)[注]此为ServletRequest的方法
其他参数参考:Servlet技术浅析(三)之—–ServletRequest接口和HttpServletRequest接口

Servlet容器如何实现HttpServletRequest接口的?

背景:使用者按照接口类型来引用请求和响应对象,即调用doGet()方法时,并不知道容器是如何实现此接口和实现了其父接口定义中的所有方法。
推荐阅读:Tomcat中容器是什么,容器间的关系
Tomcat是如何将请求一步步传递到我们编写的HttpServlet类中的
大佬写的请求传递的过程实在看不懂,简单复制粘贴如下:

  1. 请求首先由AbstractEndpoint类及其子类的内部类Acceptor(线程类)的持续监听并接收得到,过程使用套接字
  2. 当接收请求完毕,经过一系列的处理后就会由AprEndpoint的内部类SocketProcessor来将请求传给ProtocolHandler来处理。这个SocketProcessor也是一个线程类
  3. AbstractConnectionHandler接收到第二步传来的套接字以后,对套接字进行处理,并传递给AbstractHttp11Processor中的process(…)方法来处理
  4. 此时会创建org.apache.coyote.Request类型的请求
  5. 在CoyoteAdapter中,由Connector来创建成HttpServletRequest,此方法就是请求对象、响应对象和套接字进行信息交互的地方,也就是真真正正将套接字中的信息转化为请求信息,还要把响应信息写到套接字中
  6. 完成之后交给CoyoteAdapter来处理,CoyotoAdapter将请求传入Server容器的切入点
  7. CoyoteAdapter中有一个service()方法。这个方法持有一个Connector的引用。这个Connector又持有一个Service容器的引用,而Service容器有持有一个Container(Container的实现类有StandardEngine、StandardHost等等)的引用。所以CoyoteAdapter就可以根据这些引用将请求传递到Server容器中了
  8. 当最后一个StandardWrapperValve(其对应Engine会调用它持有的StandardPipeline对象来处理请求,此管道中的有许多阀门,这些Valve会对请求进行处理)处理完请求后,此时请求已经到达了最底层的容器了。
  9. StandardWrapper就是最底层的容器,它不允许再有子容器。之后把请求交给Filter来处理,此时请求进入过滤器链条中,参考:过滤器中的chain.doFilter(request,response)
  10. 过滤器处理完之后当将请求传递给我们编写的HttpServlet来处理

Cookie和Session的区别与联系

Cookie和Session都是为了保存客户端和服务端之间的交互状态,实现机制不同,各有优缺点。

  • 一个最大的区别就是Cookie是保存在客户端Session就保存在服务端的。Cookie是客户端请求服务端时服务器会将一些信息以键值对的形式返回给客户端,保存在浏览器中,交互的时候可以加上这些Cookie值。用Cookie就可以方便的做一些缓存。
  • Cookie的缺点是大小和数量都有限制;Cookie是存在客户端的可能被禁用、删除、篡改,是不安全的;Cookie如果很大,每次要请求都要带上,这样就影响了传输效率。
  • Session是基于Cookie来实现的,不同的是Session本身存在于服务端,但是每次传输的时候不会传输数据,只是把代表一个客户端的唯一ID(通常是JSESSIONID)写在客户端的Cookie中,这样每次传输这个ID就可以了。
  • Session的优势就是传输数据量小,比较安全。
  • Session也有缺点,就是如果Session不做特殊的处理容易失效、过期、丢失或者Session过多导致服务器内存溢出,并且要实现一个稳定可用安全的分布式Session框架也是有一定复杂度的。
  • 在实际使用中就要结合Cookie和Session的优缺点针对不同的问题来设计解决方案。

推荐阅读:真相了!关于HTTP 请求方式: GET和POST的比较的本质

幂等的Get 非幂等的Post

[理解]幂等:可以一遍一遍反复做同一件事,不会有预料不到的副作用,Get、Head、Put都是幂等的。Post是非幂等的,Post体的提交数据可能用于不可逆转的事务,如涉及更新DB的操作时,易造成操作不止一次的情况。
所以,Post本质上是非幂等的,需要程序人员干涉处理。

你应该知道的表单细节

  1. 表单中若没有明确给出method=‘Post’,则会默认为HttpGet请求。

  2. 使用getParameter(“parametername”)方法,返回值始终是String

  3. Post请求若有两个参数,可用&分隔,如:

    1
    color=dark&body=heavy
  4. getHeader()方法获得首部String,对于某些首部(例如Content-Length信息体的字节数)可能就对应的一个数,此时可以用getIntHeader()直接得到int值

  5. getServerPort()\getLocalPort()\getRemotePort()的区别

方法 比较
getRemotePort() 得到客户的端口,即发出请求的客户的端口号
getServerPort() 请求原来发送到哪个端口,即服务器监听的端口
getLocalPort() 请求最后发送到哪个端口,即服务器为每个线程找的一个不同的本地端口,用以实现一个应用同时处理多个客户
[注]远程意味着客户

在Response中为客户发送一个jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Servlet代码
pubilc class Codereturn extends Httpservlet{
public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{

//告诉浏览器接下来是什么类型的内容(即内容类型)
response.setContentType("application/jar");

//获得本Servlet对应的背景对象,ctx.getResourceAsStream()方法中是jar所在路径,在JarDownlioad根目录下,并表示给浏览器一个输入流
ServletContext ctx = getServletContext();
InputStream is = ctx.getResourceAsStream("/bookCode.jar");

//以下是常规的I/O操作
int read = 0;
byte[] bytes = new byte[1024];
OutputStream os = response.getOutputStream();
while((read = is.read(bytes)) != -1){
os.write(bytes,0,read);
}
os.flush();
os.close();
}
}

其他:可以设置response来设置首部、发送错误、增加cookie等

setContenttype()常用的内容类型

  • 内容类型:即MIME类型Multipurpose Internet Mail Extensions。服务器告诉浏览器要发出的是什么,浏览器才会做出正确的举动。
  • 注:总是先调好setContenttype(),再调用获得输出流的方法。
  • 服务器不一定能通过文件的扩展名看出内容类型,所以需要MIME
    MIME类型:不用记忆
名称 MIME类型
超文本标记语言文本 .html text/html
xml文档 .xml text/xml
XHTML文档 .xhtml application/xhtml+xml
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
PDF文档 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG图像 .png image/png
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar
任意的二进制数据 application/octet-stream

输出字符或是字节

1
2
3
4
5
6
7
8
//将文本数据打印到字符流
PrintWriter writer = response.getWriter();
writer.println("some text and HTML");

//文本打印到字节流
ServletOutputStream out = response.getOutputStream();
out.write(aByteArray);

  • println()写至PrintWriter
  • write()写至ServletOutputStream
  • 其实PrintWriter内部包装了ServletOutputStream,使之更便于处理字符。

请求重定向 = 客户

[理解]让别人来为你的请求处理响应,例如servlet重定向到一个完全不同的URL,或者把请求分派给Web应用的另一个组件。

  1. servlet拿到请求后决定使用重定向
  2. servlet在响应上调用sendRedirect(aString),servlet完成工作
  3. 之后Http响应有带着状态码301,以及一个“Location”首部(即一个URL)
  4. 浏览器得到响应,发现301状态码,并寻找“Location”首部;并使用该Location的值(即新的URL)建立一个新的请求(用户可以发现此时浏览器地址栏的URL发生了改变)
  5. 服务器对新的请求作出响应,在浏览器展示新页面
  • 可以使用相对URL来作为sendRedirect()的参数(该参数只能是String类型,见下例),即sendRedirect(“foo/stuff.html”),此url是相对于原URL(需要原URL放在location的首部)来建立的,
  • 若sendRedirect(“/foo/stuff.html”),此时容器会相对Web应用本身建立完整的URL,不会参考原URL。
  • 要在响应之前发出重定向的指令,因为已经向流中写了东西,想重定向为时已晚,否则抛出IllegalStateException异常。

请求分派 = 服务器

  1. servlet拿到请求后决定交给Web应用的另一个部分(下文以JSP举例)

  2. servlet调用以下代码,由JSP接管响应

    1
    2
    3
    RequestDispatcher view  = 
    request.getRequestDispatcher("result.jsp");
    view.forward(request,response);
  3. 浏览器以正常方法得到响应,而且浏览器地址栏没有变化,用户一无所知

已完成《Head First Servlets and JSP》第4章 146页

-------------The End-------------