11、Request & Response
Request & Response
- request: 由Http请求封装而成的对象,用于接收浏览器提交的数据。从这个对象可以取出(get)浏览器发送的数据。
- response: 用于生成Http响应的对象,将服务器的数据发送给浏览器。这个对象可以设置(set)发送给浏览器的数据.
Response对象
用于生成http响应信息,对于开发人员来讲,就是 向response 对象中添加信息即可。
响应首行
HTTP/1.1 200 OK
void setStatus(int sc)
void sendError(int sc)
void sendError(int sc, String msg)
响应头
void setHeader(String name, String value) 设置键值对.
void setDateHeader(String name, long date) 设置键值对.
void setIntHeader(String name, int value) 设置键值对.
void addHeader(String name, String value) 添加键值对.
void addDateHeader(String name, long date)添加键值对.
void addIntHeader(String name, int value) 添加键值对.
setHeader(name, value)
:如果Header中没有定义则添加,如果已定义则用新的value覆盖原用value值。
addHeader(name, value)
:如果Header中没有定义则添加,如果已定义则保持原有value不改变。
响应空行
响应正文
// 需要给浏览器的资源
getWriter(); 字符流
getOutputStream(); 字节流
一些例子
手动向浏览器 发送404错误状态码。
package response;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Aservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.sendError(404, "哈哈");
}
}
访问BServlet,3秒后浏览器自动跳转到百度。
package response;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Bservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// <meta http-equiv="Refresh" content="3;url=地址" >
// 设置编码防止乱码
response.setContentType("text/html;charset=utf-8");
// response.addHeader("Refresh", "3;url=https://www.baidu.com");
PrintWriter out = response.getWriter();
// 让页面有倒计时的效果
response.getWriter().write("您将在<span id='one'>3</span>秒后跳转!" +
"<script type='text/javaScript' >" +
"var span = document.getElementById('one');" +
"var i =3;" +
"function fun(){" +
"i--;" +
"if(i>=0){" +
"span.innerHTML = i;" +
"}" +
"}" +
"window.setInterval(fun,1000);" +
"</script>");
}
}
中文乱码
字节流
保证编码码表和解码码表一致,一般utf-8
package response;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Cservlet extends HttpServlet {
// 编码码表和解码码表一致就行
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置解码码表
response.setContentType("text/html;charset=utf-8");
response.getOutputStream().write("哈哈".getBytes("utf-8")); // 设置编码码表,不一致会出现乱码
}
}
字符流
setContentType
方法如果发现你填写码表,那么会同时自动设置编码码表。意思就是填写一此码表即可。
package response;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Dservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("哈哈");
}
}
结论
- 输出中文建议使用字符流
- 解决字符流乱码 使用setContentType放可以同时设置编码解码两端的码表.
- 注意: 码表的设置一定放到输出之前
- 注意: 字符流与字节流不能同时使用
向servlet发送图片
package response;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Eservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
InputStream is = getServletContext().getResourceAsStream("/wallpaper.png");
OutputStream os = response.getOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
os.flush();
// 调用别人方法开的流,一般无需关闭(他肯定会关)
// 自己new出来的流需要关闭
}
}
}
从Servlet下载文件
package response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Fservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
InputStream is = getServletContext().getResourceAsStream("/kxml2-src-2.3.0.zip");
// 原则: 凡是响应正文中需要输出内容, 一定要设置content-type头
ServletContext sc = getServletContext();
// 只会截取后缀名zip
String type = sc.getMimeType("abc.zip");
response.setContentType(type);
// 设置这里让浏览器知道我们下载的文件名,要不默认使用Fservlet这个名称,且没有后缀名
response.setHeader("Content-Disposition", "attachment;filename=kxml2-src-2.3.0.zip");
OutputStream os = response.getOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
os.flush();
}
}
}
Request对象
请求首行
请求方式 请求路径 协议/版本号
String getMethod() // 获得请求方式
request.getContextPath() // /req_res 获得项目路径
request.getQueryString(): name=tom?age=19 // 获得请求路径中的参数
request.getRequestURI(): // /req_res/AServlet 获得当前访问的相对路径,相对主机路径
request.getRequestURL(): // http://localhost:8080/req_res/AServlet 获得当前访问的绝对路径
request.getServletPath(): // /AServlet 获得servlet路径
String getScheme() // 获得协议协议(如http)
请求头
键 : 值
request.getContentLength() // 返回正文内容长度,-1表示没有
request.getContentType() // null 返回正文内容类型
request.getLocale() // zh_HANS_CN // 获得accept-language:zh_CN
request.getServerName() // localhost 获得主机名称
request.getServerPort() // 8080 获得访问端口号
String getHeader(String name) // 根据键获得值(值只有一个)
long getDateHeader(String name)
int getIntHeader(String name)
Enumeration getHeaderNames() // 获得所有请求头的名字
Enumeration getHeaders(String name) // 根据键获得值(值有多个)
请求空行
请求正文
post请求才有=> 请求参数 => name=tom&age=18
String getParameter(String name) // 根据参数键获得参数值
Map<String,String[]> getParameterMap() // 返回封装参数的Map
Enumeration getParameterNames() // 获得所有参数的键
String[] getParameterValues(String name) // 根据参数键获得参数值(值有多个)
一图胜千言。
获得表单数据
使用了GET和POST两种请求方法。
下面是两个表单,提交方式分别是GET和.
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
...
<body>
<form action="/req_res/Gservlet" method="get">
用户名:<input type="text" name="name" /> <br />
密码:<input type="password" name="password" /> <br />
<input type="submit" />
</form>
<hr>
<form action="/req_res/Gservlet" method="post">
用户名:<input type="text" name="name" /> <br />
密码:<input type="password" name="password" /> <br />
<input type="submit" />
</form>
</body>
</html>
package request;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Gservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Enumeration<String> en = request.getParameterNames();
while (en.hasMoreElements()) {
String key = en.nextElement();
String value = request.getParameter(key);
System.out.println(key + " : " + value);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Enumeration<String> en = request.getParameterNames();
while (en.hasMoreElements()) {
String key = en.nextElement();
String value = request.getParameter(key);
System.out.println(key + " : " + value);
}
}
}
上面的表单中输入中文,输出会乱码。
解决乱码--GET
可以在/comf/server.xml
里面,配置服务器的解码码表。这针对范围很广。
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
connectionTimeout="20000"
redirectPort="8443" />
解决乱码--POST
POST提交和GET提交区别只是服务器解码时间点不一样,其他没区别。
GET: 请求一旦到达服务器会立即解码参数
POST: 调用获得参数时候才会解码
所以解决POST提交乱码,只需要调用参数方法前,设置一下解码码表就。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
Enumeration<String> en = request.getParameterNames();
while (en.hasMoreElements()) {
String key = en.nextElement();
String value = request.getParameter(key);
System.out.println(key + " : " + value);
}
}
请求转发
意义在于使得Servlet和JSP分工明确。
- Servlet处理业务逻辑--本质上是Java代码,主要是在service(req, res);中操作
- JSP负责显示结果
这里还没学JSP技术,暂时用Eservlet代替JSP使用。
下面是Dservlet
package response;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Dservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("我负责处理业务逻辑");
// 装发给Eservlet
request.getRequestDispatcher("/Eservlet").forward(request, response);
}
}
下面是Eservlet
package response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 访问Dservlet转发过来
public class Eservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我负责显示结果");
}
}
Dservlet和Eservlet怎么共享数据
和SevletContext类似,Request域,在request对象中哟一个map,把处理结果放在Dervlet中,再从Eservlet中取出来。
Dservlet
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我负责处理业务逻辑");
String name = "value"; // 假设这个值是从数据库中取出来的
// 将name放入Request域中
request.setAttribute("key", name);
request.getRequestDispatcher("/Eservlet").forward(request, response);
}
Eservlet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("我负责显示结果");
String value = (String) request.getAttribute("key");
System.out.println("Eservlet: " + value);
}
转发时候要注意
- 不能在正转发的Servlet中(比如这里的Dervlet),向浏览器输入任何正文响应。因为即将转发了,也不会显示出来。但是接收 转发的Eservlet中可以向浏览器输入响应。但是添加头是允许的
- 在转发Servlet后,不允许再操作request和response对象。直白点说就是
request.getRequestDispatcher("/Eservlet").forward(request, response);
这句之后,一般不在其后写代码了,通常这句是最后一行。
重定向和转发的区别
-
分别是发送了几次请求
- 重定向:两次
- 转发:一次
-
能不能访问项目之外的地址
- 重定向:可以
- 转发:不可以(转发是内部的事)
- 能不能使用request域共享数据
- 重定向:不可以
- 转发:可以
- 地址栏会不会发生变化
- 重定向: 一般会
- 转发:不会
-
请求方式是否会发生改变
- 重定向: 有可能,重定向的第二个请求一定是GET
- 转发:不会
请求包含
往往是在多个jsp中使用,其中一个jsp封装相同部分,其他的jsp凡是要是显示相同的部分时,就可以把装着这相同部分的jsp包含进来。
包含与转发的区别
- 转发中只能设置头,而不能向响应正文输出 < -- > 包含是两者都可发送
- 转发后不能在对req和res再进行操作 < -- > 包含中没有这个限制
一个包含的小例子
其中Jservlet作为封装相同内容的那个。
package request;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Jservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("这是相同部分");
}
}
Iservlet
package request;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Iservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().println("这是第一部分");
// 包含
request.getRequestDispatcher("/Jservlet").include(request, response);
response.getWriter().println("这是第二部分");
}
}
这里只在Iservlet中指定码表即可,因为是同一个响应,所以不必用Jservlet指定,也会正常显示不会出现乱码。
by @sunhaiyu
2017.3.27