> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/java/web-bian-cheng/servlet-xiang-jie-er-request-he-response-dui-xiang.md).

# Servlet详解(二)：request和response对象

### 什么是request和response

request对象是服务器对浏览器请求的封装，而response是服务器对服务器响应的封装。

![这里写图片描述](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-8ec2a45a6163a3265c8c6583eed72dd57a493514%2Fformat%2Cpng-20211210104209158.png?alt=media)

request用来取出请求信息，而response则用来添加要返回给浏览器的信息。

### 使用response对象设置http响应协议中的信息

用来生成Http响应信息，发送给用户。

响应首行

> HTTP/1.1 200 OK

* `setStatus(int sc)`：设置正常的响应状态码 status code
* `setStatus(int sc， String sm)`：设置正常的响应状态码,状态码描述 status message,过时，因为正常状态下，状态码信息不会显示给用户，所以没有必要设置
* `sendError(int sc)`:设置错误的状态码
* `sendError(int sc, String sm)`：设置错误的状态码,包含错误信息

响应头

> key:value

* `setHeader(String name, String value)`:设置一个键值对，值为string
* `setDateHeader(String name, long date)`：设置一个键值对，值为long，long常用于毫秒的表示
* `setIntHeader(String name, int value)`：设置一个键值对，值为int类型
* `setHeader(String name, String value)`:添加一个键值对，值为string
* `setDateHeader(String name, long date)`：添加一个键值对，值为long，long常用于毫秒的表示
* `setIntHeader(String name, int value)`：添加一个键值对，值为int类型
* add 和 set 区别在于，前置直接添加（key是可以重复的），后者会修改原来的，没有才会添加。

几个常见的响应头：

* ContentType:text/html;charset=utf-8
* Refresh:3;url= <http://path> 3秒后跳转网页

html中meta标签的作用就是用于向响应头中添加信息。

响应空行

> 一个分隔符

响应正文

> 需要发送的资源

* `response.getWriter()`：获取字符流
* 父类方法 `getOutputSteam()`：获取输出字节流

#### 实现请求重定向

**手动实现：**

实现重定向很简单，只需要两个步骤：

1. 在响应头中添加302状态码，告诉浏览器需要进行重定向

   `response.setStatus(302)`
2. 在响应头中添加Location，指定重定向的位置

   `response.setHeader("Location", "http://www.baidu.com");`

当用户请求servlet时，服务器返回一个类似上面的响应头，上面的信息告诉浏览器，应该重新进行一次请求，请求的地址为 Header中的Location地址。

所以，请求重定向实际上是两次请求。

**直接实现：**

我们只需要使用: `response.setRedirect("http://www.baidu.com")` 即可实现。

#### 通过访问Servlet向浏览器传递一个图片

```java
// 获取图片输入流
InputStream is = getServletContext().getResourceAsStream("/WEB-INFO/mm.jpg");
// 获取浏览器的输出流
byte[] buffer = new byte[1024];
// 将图片篇输入流写出到浏览器中
int len = -1;
while((len = is.readBuffer(buffer)) != -1){
    os.write(buffer, 0, len);
    os.flush();
}
```

#### 通过访问Servlet下载文件

类似于图片，但是浏览器不能识别文件的名称和文件的类型，需要我们在响应头中设置文件的类型：

```java
ServletContext sc = getServletContext();
String mimeType = sc.getMimeType("下载的文件名称"); // 传入下载的文件名称，会根据名称的拓展名识别mime类型
response.setContentType(type);
```

即可。

### 通过request对象获取http请求协议中的内容

用来生成http请求信息。

请求首行

> 请求方式 请求路径 协议/版本号

* `request.getMethod()` 请求方式
* `request.getContextPath()`， `getServletPath()`…获取请求路径
* `父类方法 request.getScheme()` 获取请求协议

请求头

> 键值对

* `request.getHeader(String name)`
* `request.getIntHeader(String name)`
* `request.getDateHeader(String name)`
* `request.getHeaderNames()`
* `request.getHeaders(Sring name)` 一个key对应多个value

请求空行

请求正文（post才有）

> 但是以下方法，无论是get还是post请求都可以获得参数值

* `父类方法 String getParameter(String name)`
* `Map<String, String[]> getParameterMap()`
* `Enumeration getParameterNames()`
* `String[] getParameterValues(String name)`

### 解决响应和请求的乱码问题

#### 处理get请求编码问题

解决方案:在 `conf/server.xml中URIEncoding="UTF-8"` 69行，

```xml
<Connector URIEncoding="UTF-8"
    connectionTimeout="20000"
    port="8888"
    protocol="HTTP/1.1"
    redirectPort="8443"/>
```

设置URIEncoding为UTF-8

#### 处理post请求编码问题

服务器响应时的数据，即服务器向浏览器传递的数据的编码格式由服务器决定：

编码时使用的编码表，使用 `getByte("编码表")` 设置，或者使用 `response.setCharacterEncoding(编码表)` 设置。两者的区别在于 ，前者设置字节流码表，后者设置字符流码表。

解码时使用的编码表，使 `response.setHeader("ContentType","text/html;charset=utf-8")` 指定http响应头来设置。

![](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-efbac72bbbf261ad492fa3535055db8027587282%2FaHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYxMjA2MjEzNzU5MDYw.png?alt=media)

***

客户端发送请求时的乱码解决：

**GET提交**，参数在URL中，设置URL的解码配置，服务器默认使用IOS-8855-1拉丁码表解码URL，我们可以通过 `tomcat/config/server.xml` 配置文件中:

```xml
 <Connector
     connectionTimeout="20000"
     port="8080"
     protocol="HTTP/1.1"
     redirectPort="8443"/>
```

添加属性 `URIEncoding="UTF-8"` 即可将服务器默认的解码url的方式设置为utf-8

或者在doget方法中

将接收的乱码文字使用新的码表转换：

```java
String name = request.getParamter("name"); // 获取乱码文字
byte[] bs = name.getBytes("IOS-8859-1"); // 根据乱码码表，将文字转换为字节数组
    String s = new String(bs, "UTF-8"); // 将字节数组按照新的码表解码，生成文字
```

![](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-0f4a73606044ec0fb2f37647a7ba30ba2f6aca06%2F20161208171242792.png?alt=media)

**POST提交**：

与GET提交解码的区别：

解码事件不同，GET因为参数在URL中，所以服务器一旦接受请求就会立刻解码参数，而POST在Servlet调用获取参数的方法时才会解码。

所以，解决post请求的乱码很简单，只需要在参数调用前使用

```java
request.setCharacterEncoding("utf-8");
```

设置请求解码表即可。

### request 请求转发实现和request域

![](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-af889804a7c4f63b14d1cb45daad2900cc45aa74%2FaHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYxMjA4MTczODQ4NTkw.png?alt=media)

这就是请求转发，浏览器发送请求，servlet处理request和response部分业务，但是无法全部处理，也无法简单的显示到页面中，所以将处理过得request和response发送给jsp，jsp进行一些业务操作，并响应给浏览器，展示。

请求转发主要用来分工操作。

AServlet doGet()：

```java

// ***** 不标准自定义转发
//todo AServlet进行业务处理，比如从数据库获取数据
// 处理结束后：=>
// 发送给Bservlet
BServlet bServlet = new BServlet();
bServlet.doGet(request, response);

//*****标准转发
// 使用request域(一个请求内有效，主要用于请求转发) 保存数据库的信息，发送给BServlet，即 AServlet和BServlet使用request域共享数据，request域是request对象中的一个Map。
request.setAttribute("name","Feathers");  // 向request域中存入一个键值对
request.getRequestDispatcher("/servlet/BServlet").forword(request.response);
```

BServlet doGet()：

```java
// todo 负责输出显示
// 从request域中取出值
System.out.println((String)request.getAttribute("name"));
```

**注意：**

不能在转发的Servlet中向浏览器输出任何响应正文的内容，但是可以添加响应头。

因为Servlet中，即使你添加了响应体，也会被清空。

### 重定向和转发的区别

* 重定向是一次请求，转发一次请求
* 重定向可以访问项目之外的地址，而转发不能。
* 重定向不可以使用request域，而转使用request域，一次请求，一个request对象
* 重定向地址栏可能会发生会发生变化，而转发一定不会
* 重定向请求方式有可能发生改变（重定后的请求一定是get请求，只有显示设置post才是post请求，否则是get请求），而转发不会
* 重定向时response的方法（通过修改response头来完成重定向），而请求转发是resquest的方法（一次请求，转发给不同的Servlet）

### 请求包含

请求转发，转发的Servlet不能修改请求体，而请求包含中，同请求转发类似，但是可以修改请求体。

用途：用来解决重复操作。

将重复的操作提取到这个Servlet中，统一进行处理。

![](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-51bd0e657db14155bd1f2d22d592d40ce61ca04b%2FaHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYxMjA4MTgyNDAwOTg.png?alt=media)

往往在多个JSP中使用，一个jsp用于处理相同的操作，其余的jsp根据是否需要处理，进行依次处理。

实现：

AServlet doGet()：

```java
response.setContentType("text/html;charset=utf-8");
// 包含
request.getRequestDispatcher("/servlet/BServlet").include(request,response);
response.getWriter().write("Aservlet 处理");
```

BServlet doGet():

```java
response.getWriter().write("Bservlet 处理");
```

结果：

```
Aservlet 处理Bservlet 处理
```
