Hea First Servlet&JSP 4.request And response

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_34524528/article/details/86365941

Servlet live to service clients.
需要记住第二章里没记住的。本章的细节不会在之后的章节再次复述。
勤加训练,记住没有记住的。模拟考试并达到 80% 以上的正确率之前,不能进入下一章的学习。

描述servlet生命周期的目的和事件序列-loading-instantiation-init()-service()-destroy()

Servlet 只有两种状态,要么不存在(does not exist),要么存在(initialized)。不存在经由 init() 到存在,存在经由 destroy() 到不存在。

Servlet 的声明周期

类的继承、实现关系
MyServlet->HttpServlet->GenericServlet->Servlet<<interface>>
Servlet 继承关系

三大生命周期时刻

When it’s called What it’s for Do you override it
init() after the servlet instance is created bu before the servlet can service any client requests Gives you a chance to initialize your servlet before handing any client requests Possibly(getting a DB connection / registering yourself with other objects
service() the first client request comes in determines the HTTP methods and invokes the matching doGet(), doPost(), etc. No.Very unlikely.
doGet() and/or doPost() service() invokes doGet() or doPost() responsible for whatever the heck your web app is supposed to be DOING. All starts from here. ALWAYS

So the service() -> doGet() method sequence happens each time there’s a client request.

注:这里牵涉到线程的管理(create & allocate)相关知识,需要恶补。

  1. init() 初始化
  2. Servlet初始化之后,第一个 request 的到来让 Container 分配线程并 invokes service()
  3. 然后,service() 又根据请求的类别 Invokes 与请求类别匹配的 doXXX() 方法(我自己覆盖过的方法),
  4. 此后 service() 进程销毁或者放进由 Container 管理的线程池中,
  5. 下一个request来,再从 2 (创建或找一个线程运行 service())来一次,

视资源(resources)限制和容器策略(policies)和配置(configuration)的不同,可运行线程的数目至少和客户端请求的数目一样多,如果请求数超过指定的最大同时运行线程数,那么一些客户端不得不等待。

每个请求在一个单独的线程中运行。容器运行多个线程来处理对单个 servlet 的多个请求。故 “Each instance of the servlet…" 这种说法是错误的。
一个servlet多个线程处理多个请求

一个请求一个线程,而不是一个客户一个线程,容器不关心请求时谁发出的——有请求进来就创建一个 thread/stack。

servlet 的生命开始于查找 class files, 通常在 Container 启动时(比如打开 Tomcat)。
Container 启动时,其查看(look)已经部署的 web apps,并查找(searching) servlet class 文件,这是第一步。
第二步,加载(loading),视配置不同,可以在Container startupfirst client use 时发生,无论是那种加载方式,在 servlet 初始化完全完成前,service() 方法都不会运行。亦即 :init() always completes before the first call of service().

初始化,是一个简单对象变成 Servlet 的过程。这个过程中,Servlet 是存在(initialized)和不存在(do not exist)的一个中间状态(“薛定谔的 Servlet”),在成为 Servlet 的过程中,对象会被授予特权,例如,用 ServletContext 引用从容器中获取信息。
记住一点,servlet 的 constructor 里面没有有参数。没有什么事情不能等到 init() 完成之后。

一个正式的 Servlet 比普通的对象(object)多了:

  • A ServletConfig Object (部署时起避免硬编码的作用,如DB,只要此servlet已部署并运行,它就不会更改。一个 servlet 一个)
  • A ServletContext (AppContext, 一个 web app 一个)
    这章只是起一个头,下一章会明说。

各种HTTP方法的用途和特点

HttpServletRequest-从请求获取信息

回到真正的工作 handle requests 上来。

  • 容器实现了 HttpServletRequestHttpServletResponse ,不必操心具体实现过程。
  • 接口可以继承接口。
  • 不讨论 HTTP 以外的协议
  • 不讨论 GET 和 POST 以外的方法(as a web-server/servlet developer)

GET 和 POST 的区别:POST 有 body.
GET 的参数塞在path后面,POST 的参数放在body里面。

GET 参数:大小有限,明码可见,可以被 bookmark (搜索结果页就很适合,整个bookmark起来,下次不必重新输入搜索关键字)——说一千道一万,GET就是用来简单粗暴地“取回”内容的,不会对服务端作任何改变。POST 是为了传需要被处理的数据,它可以用来传递一个查询参数,达到和GET相同的效果,但根本是“上传”,就是要对服务端作出改变。

关键在区分:whether the reqeust is idempotent

支付页面没有即使反馈“正在支付”或者“支付成功”,会让用户以为窗口冻结,反复点按购买按钮,导致重复下单。

幂等性 idempotent 是好的。这意味着你可以一遍又一遍地做同样的事情,而不会产生不想要的副作用!(GET 就是如此)

Idempotent vs NOT Idempotent
POST is not idempotent.

注:这里的 idempotent 指的是 the same request can be made twice with no negative consequences on the server,不是 the same request always returns the same response,也不是 a request has NO side effects

普通的 <a> 标签中的 href 都是 GET 请求。
<form> 属性中如果没有 method=“POST” 属性,默认为 GET 请求。这种情况下,很可能因为 servlet 端没有重写 doGET() 方法而造成错误。为了避免请求失败,可以如下书写代码:

public void doPost(...)
				throws ... {
	doGet(request, response);
}

一个 form 里,可以有多个参数(几个 select),一个参数里可以有多个值(如checkbox)

<form method="GET" action="SelectBeer.do">
    <input type="checkbox" name="sizes" value="12oz">12 oz.<br>
    <input type="checkbox" name="sizes" value="16oz">16 oz.<br>
    <input type="checkbox" name="sizes" value="22oz">22 oz.<br>
    <input type="submit" value="ni "></form>

勾选✔后发出去的效果
SelectBeer.do?sizes=12oz&sizes=16oz&sizes=22oz
代码中这样写:

String one = request.getParameterValues("sizes")[0];
String [] sizes = request.getParameterValues("sizes");
// for testing
String [] sizes = reqeust.getParameterValues("sizes");
for(int x=0; x < sizes.lenght ; x++) {
	out.println("<br>sizes: " + sizes[x]);// assume that "out" is a PrintWriter you got from the response
}

除了参数,还能从 Request 里面得到什么?

//要查看全部 API 看如下两个类
javax.servlet.ServletRequest
java.servlet.ServletResponse

需要关注的常用的方法有(常用的可能15%都不到)

// To client's platform and browser info
String client = request.getHeader("User-Agent");
// The cookies associated with this request
Cookie[] cookies = request.getCookies();
// The session associated with this client
HttpSession session = request.getSession();
// The HTTP Method of the request
String theMethod = request.getMethod();
// An input stream from the request
InputStream input = request.getInputStream();

Header 有name 和value,如 “User-Agent":"http://t.cn/EUC9o12?m=4317179900218395&u=3921730119"getHeader() 得value字符串。但对于有一些值本来就是数字的header,如"Content-Length"(指定message-body的字节数)、“Max-Forward”(指定通过路由的跳数),要直接获取数值,可以方便地用 getIntHeader() 方法。如下所示:

String forwards = request.getHeader("Max-Forwards");
int forwardsNum = Integer.parseInt(forwards);
// 等效于
int forwardsNum = request.getIntHeader("Max-Forwards");

区分:

  • getRemotePort() :get the client’s port,服务器的 remote 自然是 client。
  • getServerPort():to which port was the request originally SENT? 请求最初发送到哪个端口
  • getLocalPort():on which port was did the request END UP? 请求是在哪个端口上完成的

because although the requests are sent to a single port (where the server is listening), the server turns around and finds a different local port for each thread so that the app can handle multiple clients at the same time.
因为虽然请求被发送到单个端口(服务器正在侦听),但服务器转向并为每个线程找到不同的本地端口,以便应用程序可以同时处理多个客户端。

HttpServletResponse-设置响应-获取流

说完了 Request 来说 Response

大多数时候,你只是用 Response 返回数据给客户端。
使用两个方法:setContentType()getWriter(),利用I/O直接写进stream里。
但是有时候会用 Response 设置其他的 headers,发送errors,以及 add cookies。

response interface

前文提到过,不会直接把HTML写进Stream,而用 JSPs. 但不是说绝对不直接往 Response 里写东西,有如下原因:

  1. hosting provider 不支持 JSPs,有些旧的服务器或容器支持 servlet 但不支持 JSPs。
  2. 没有使用 JSPs 的选项,如系统管理员拒绝。
  3. 不写HTML,可以写其他东西,如:a JAR。

发送 JAR 到客户端。
例如创建一个下载页面让客户端能够通过JAR文件下载代码,而不是发送 HTML 网页。读取 JAR 的字节,然后写进 response 的 output stream 里面。

import javax.servlet.ServletRequest;
import java.io.InputStream;
import java.servlet.ServletResponse;

public class CodeRetrun extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletExcetion {
        response.setContentType("application/jar");// "/" 表示web app 的根目录

        ServletContext ctx = getServletContext();
        InputStream is = ctx.getResourceAsSteam("/bookCode.jar");

        int read = 0;
        byte[] bytes = new byte[1024];

        OutputStream os = response.getOutputStream();
        while ((read = is.read(bytes)) != -1) {
            os.wirte(bytes, 0, read);
        }
        os.flush();
        os.close();
    }

}

说 Content Type 就是说 MIME type。Content Type 是 HTTP response 中必须包含的 HTTP header . 浏览器接收到后,方能进行正确的操作:launch a “helper” app like a PDF viewer or video player, render the HTML, save the bytes of the response as a downloaded file, etc.

Common MIME types:

  • text/html
  • application/pdf
  • video/quicktime
  • application/java
  • image/jpeg
  • application/jar
  • application/octet-stream
  • application/x-zip

不用记住 content types,但是要知道 setContentType() 是干什么的,怎么用。谨记,call setContentType() first, BEFORE you call the method that gives you your output stream (getWriter() or getOutputStream()).

Apache 可以配置映射文件后缀content type,从而不必手动设置 content type.但是当没有文件时,需手动。
当Servlet读文件时,Container 不知道我们在干嘛,只知道我们正在从一种类型的东西里读,往 response 里的另一种完全的不同的东西里写。

以发送 JAR 为例,实际上,可以让用户点击链接,直接在服务器下载JAR文件而不必经由 servlet,就像下载 JPEG 一样。但是,例子是假设你想在发送前对文件做点什么(动态),而并非是直接发送(静态),如动态发送某一个文件,动态创建并发送。换句话说,要发送的文件在发送前并非是保存在服务器上的,而是运行时构造出来的。

输出:characters or bytes

ServetResponse interface 给了两个选择:ServletOutputStream for bytes, or a PrintWriter for character data.

// printing text data to a character stream
PrintWriter writer = response.getWriter();
writer.println("some text and HTML");
// write anything alse!
ServletOutputStream out = response.getOutputStream();
out.write(aByteArray);

注意 write() to an ServletOutputStream ; printIn() to a PrintWriter
getOutputStream()getWriter() 也要记住。

区分并熟记:

  • response.setHeader(“foo”,“bar”); // 如果同名已经存在则替换,否则添加
  • response.setIntHeader(“foo”,“bar”);// 如果同名已经存在则替换,否则添加
  • response.addHeader(“foo”,“bar”);// 新增 header 及其 value,或为已经存在的header增加额外的 value

setHeader() overwrites the existing value.
addHeader() adds an additional value.

setContentType(“text/html”) equels to setHeader(“content-type”,“text/html”),后者如果"content-type"拼错了,将会添加进一个新的不应该存在的header,造成错误。

HttpServletResponse-转发和重定向

有些时候并不想自己来处理 response

要么 redirect 到一个全新的 URL,要么 dispatch the request 给 web app 的另一个组件(典型的是一个 JSP)。

重定向

Servlet redirect makes the browser do the work.
if (workForMe) {
	//handle the request
} else {
	response.sendRedirect("http://www.oreilly.com");
}

Using relative URLs in sendRedirect()
可以用相对路径,也可以用绝对路径。使用相对地址时,容器会自动构造完成的URL。如:
用户访问:http://www.wickedlysmart.com/myApp/cool/bar.do
重定向:sendRedirect("foo/stuff.html")
容器构造完整URL(HTTP response 的 “location” header 要用):http://www.wickedlysmart.com/myApp/cool/foo/stuff.html (有 /cool)

注:the container knows the original request URL started from the “myApp/cool” path, so if you don’t use a forward slash, that part of the path is prepended to the front of “foo/stuff.html”.
容器知道从“myApp / cool”路径开始的原始请求URL,因此如果您不使用正斜杠,则路径的该部分将被添加到“foo / stuff.html”的前面。

但重定向相对路径开头如果加了正斜杠:sendRedirect("/foo/stuff.html")
容器构造相对于 web app 本身,而非相对于请求原来的URL的完整URL:http://www.wickedlysmart.com/myApp/foo/stuff.html (没有 /cool)

注:The forward slash at the beginning means “relative to the root of this web app”(in this case: “myApp”) 开头的正斜杠表示“相对于此Web应用程序的根目录”(在本例中为“myApp”)

sendRedirect() takes a String NOT a URL object!
典型错误:

sendRedirect(new URL("http://www.oreilly.com"));

如果字符串不能正确构造,会 sendRedirect() 会扔出 IllegalStateException.

不可能在 response 已经 commit 之后调用 sendRedirect(aStringURL),即如果已经开始往流里写东西,重定向就迟了。

A request dispatch does the work on the server side

redirect = client,(URL 改变)
request dispatch = server.(URL 不变)

转发P144 测试
1.B
2.C
3.D
4.B
5.B
6.B
7.B
8.A/B/D/E B
9.A/B/C/D A/B/C
10.D

正确率80%

8 题错误原因:审题不清
9题错误愿意:as opposed to 与之相反与之对应,审题不清。知识漏洞,流 并非HTTP 独有。

错误收录

猜你喜欢

转载自blog.csdn.net/sinat_34524528/article/details/86365941
今日推荐