HttpServletResponse
HttpServletResponse扩展ServletResponse接口以在发送响应时提供特定于HTTP的功能。例如,它有访问HTTP头和cookies的方法。servlet容器创建一个HttpServletResponse对象,并将其作为参数传递给servlet的服务方法(doGet、doPost等)。
回传数据的方法
输出字符
在服务器端,数据是以哪个码表输出的,那么就要控制客户端浏览器以相应的码表打开,比如:outputStream.write(“中国”.getBytes(“UTF-8”));使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出,此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码,那么在服务器端如何控制客户端浏览器以以UTF-8的编码显示数据呢?可以通过设置响应头控制浏览器的行为,例如:response.setHeader(“content-type”, “text/html;charset=UTF-8”);通过设置响应头控制浏览器以UTF-8的编码显示数据。
无论使用字节流还是字符流进行数据的传递,浏览器编码方式必须通过response指定,如果不指定的话,浏览器不知道是以什么方式编码数据,最终会乱码。
respone.setHeader("Content-Type", "text/html;charset=utf-8");
//=====等同于=======
/*
*如果只respone.setCharacterEncoding("utf-8");设置了编码,而没有设置ContentType
*浏览器不知道传输的是什么类型,不清楚是以文本还是avi还是其他格式对字节码进行utf-8编码,
*所以最终还是会出现乱码的情况
*/
respone.setContentType("text/html");
respone.setCharacterEncoding("utf-8");
//=====等同于=======
respone.setContentType("text/html;charset=utf-8");
使用OutputStream字节流流向客户端浏览器输出"中国"这两个汉字
public class ResponseDemo01 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("Content-type", "text/html;charset=UTF-8")
//以字节流输出时,必须以客户端的编码方式进行输出,这样客户端才能正确编码。
String data = "中国";
OutputStream outputStream = response.getOutputStream();//获取OutputStream输出流
byte[] dataByteArr = data.getBytes("UTF-8");//将字符转换成字节数组,指定以UTF-8编码进行转换
outputStream.write(dataByteArr);//使用OutputStream流向客户端输出字节数组
}
}
data.getBytes()是一个将字符转换成字节数组的过程,这个过程中一定会去查码表,
如果是中文的操作系统环境,默认就是查找查GB2312的码表,
将字符转换成字节数组的过程就是将中文字符转换成GB2312的码表上对应的数字
比如: "中"在GB2312的码表上对应的数字是98
"国"在GB2312的码表上对应的数字是99
getBytes()方法如果不带参数,那么就会根据操作系统的语言环境来选择转换码表,如果是中文操作系统,那么就使用GB2312的码表
使用PrintWriter字符流向客户端浏览器输出中文数据
resp.setHeader("Content-type", "text/html;charset=UTF-8");
// PrintWriter的方法比write支持的参数更多,但是println()
// 底层是通过调用write实现的
resp.getWriter().write();
resp.getWriter().println();
文件下载
实现思路:
1)获取文件在server上的绝对路径(真实路径)
2)获取要下载的文件名
3)设置响应头,控制浏览器的下载行为。
4)获取下载文件的输入流
5)创建数据缓冲区
6)通过response对象获取OutputStream流
7)读取输入流的数据写入到缓冲区
8)将缓冲区的数据写入到输出流
实例:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 设置浏览器的下载行为,1)attachement 2)inline
// 中文名需要通过URLEncoder进行转码
resp.setHeader("Content-Disposition", "attachement;filename="+ URLEncoder.encode("数据库配置文件.txt", "utf-8"));
ServletContext servletContext = getServletContext();
String realPath = servletContext.getRealPath("jdbc.properties");
System.out.println(realPath);
File file = new File(realPath);
FileInputStream in = new FileInputStream(file);
ServletOutputStream out = resp.getOutputStream();
byte [] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf, 0, buf.length)) != -1) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
inline:内链的形式打开,如果浏览器能够解析文件类型的话,则可以直接在浏览器中显示
attachement:通常意义上的下载,弹出下载提示框
验证码
public class VerifyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置refresh响应头控制浏览器每隔5秒钟刷新一次
resp.setHeader("refresh", "5");
//1.在内存中创建一张图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_BGR);
//2.用画笔画图
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, 80, 20);
g.setColor(Color.BLUE);
g.setFont(new Font(null, Font.BOLD, 20));
g.drawString(makeNum(), 0, 20);
//3.设置响应头控制浏览器浏览器以图片的方式打开
resp.setContentType("image/jpeg");
//4.设置响应头控制浏览器不缓存图片数据
resp.setDateHeader("expires", -1);
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
//5.将图片写给浏览器
ImageIO.write(image, "jpg", resp.getOutputStream());
}
private String makeNum() {
Random random = new Random();
// 生成四位随机数
return (random.nextInt(8999) + 1000) + "";
}
}
**设置http响应头控制浏览器禁止缓存当前文档内容 **
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
设置http响应头控制浏览器定时刷新网页和跳转(refresh)
response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次
//网页定时刷新后跳转
resp.setHeader("refresh", "5;url=http://www.baidu.com");
路径问题
请求转发和重定向
web目录结构
-src
--...
-webapp
--index.jsp(只存在于web根目录下)
--WEB-INF
----web.xml
----classes
----lib
浏览器请求(1)、(2)路径后,执行请求转发和重定向的结果。
(1)http://localhost/appname/path
请求转发:一级路径下加不加“/”都可以请求到页面
req.getRequestDispatcher("index.jsp").forward(req, resp);
req.getRequestDispatcher("/index.jsp").forward(req, resp);
重定向:一级路径下加“/”会导致404,而不加斜杠
和req.getContextPath() + "/index.jsp"组合的路径可以请求到页面
resp.sendRedirect("index.jsp");
resp.sendRedirect(req.getContextPath() + "/index.jsp");//200
resp.sendRedirect("/index.jsp");//404
(2)http://localhost/appname/path/subpath
//请求转发:不带斜杠的404
req.getRequestDispatcher("/index.jsp").forward(req, resp);
req.getRequestDispatcher("index.jsp").forward(req, resp);//404
//重定向:只有req.getContextPath() + "/index.jsp"这种url形式才能请求到页面
resp.sendRedirect(req.getContextPath() + "/index.jsp");
resp.sendRedirect("index.jsp");//404
resp.sendRedirect("/index.jsp");//404
ServletRequest
下的getRequestDispatcher
方法源码
/**
* Returns a {@link RequestDispatcher} object that acts as a wrapper for the
* resource located at the given path. A <code>RequestDispatcher</code>
* object can be used to forward a request to the resource or to include the
* resource in a response. The resource can be dynamic or static.
* <p>
* The pathname specified may be relative, although it cannot extend outside
* the current servlet context. If the path begins with a "/" it is
* interpreted as relative to the current context root. This method returns
* <code>null</code> if the servlet container cannot return a
* <code>RequestDispatcher</code>.
* <p>
* 路径可以是相对路径,以"/"开头的路径按照则按照当前应用上下文路径被解释执行。
* The difference between this method and
* {@link ServletContext#getRequestDispatcher} is that this method can take
* a relative path.
* ServletContext#getRequestDispatcher中的path必须以 “/”开头
*
* @param path
* a <code>String</code> specifying the pathname to the resource.
* If it is relative, it must be relative against the current
* servlet.
* @return a <code>RequestDispatcher</code> object that acts as a wrapper for
* the resource at the specified path, or <code>null</code> if the
* servlet container cannot return a <code>RequestDispatcher</code>
* @see RequestDispatcher
* @see ServletContext#getRequestDispatcher
*/
public RequestDispatcher getRequestDispatcher(String path);
path可以是相对路劲也可以是绝对路径(req.getContextPath()+xxx),ServletContext类下的getRequestDispatcher
方法只能是以/开头,WEB-INF目录下的文件对于请求转发可见,斜杠开头的路径是相对于当前web应用上下文context-root
的,不以斜杠开头的路径则是相对于当前url来说,就是相对于浏览器的url路径来说的。
HttpServletResponse
下的sendRedirect
方法 看文档注释
/**
* Sends a temporary redirect response to the client using the specified
* redirect location URL. This method can accept relative URLs; the servlet
* container must convert the relative URL to an absolute URL before sending
* the response to the client. If the location is relative without a leading
* '/' the container interprets it as relative to the current request URI.
* If the location is relative with a leading '/' the container interprets
* it as relative to the servlet container root.
* <p>
* If the response has already been committed, this method throws an
* IllegalStateException. After using this method, the response should be
* considered to be committed and should not be written to.
*
* @param location
* the redirect location URL
* @exception IOException
* If an input or output exception occurs
* @exception IllegalStateException
* If the response was committed or if a partial URL is given
* and cannot be converted into a valid URL
*/
public void sendRedirect(String location) throws IOException;
斜杠开头的路径将被解释为容器的根路径请求,容器的根路径可以看成是webapps下路径,相对于tomcat容器来说,也可以看成是localhost:8080/
的路径;重定向和普通的a标签href属性的请求都是差不多的,仍然不可以访问WEB-INF目录下的文件,如何去处理重定向容易404的问题?
request.getContextPath() + /xxx
有效的解决了404问题,并且request.getContextPath()
比硬编码路径更容易修改和维护。
在idea中,可以灵活的命名部署的项目,如果命名为只有一个斜杠,那么对于
resp.sendRedirect("/xxx")
等同于resp.sendRedirect(req.getContextPath + "/xxx")
,都能够访问到页面。
container-root和context-root
container-root:容器根路径->localhost:8080/。
context-root:web应用根路径->localhost:8080/app/
细节
getOutputStream
和getWriter
方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter
对象。
getOutputStream
和getWriter
这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 Servlet程序向ServletOutputStream
或PrintWriter
对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
Serlvet的service
方法结束后,Servlet引擎将检查getWriter
或getOutputStream
方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。
HttpServletRequest
获取请求行常用方法
方法声明 | 功能描述 |
---|---|
String getMethod() | 获取请求方式(get/post…) |
String getRequestURI() | 获取请求行中资源名称部分,即位于URL主机和端口号后参数之前的部分 |
StringBuffer getRequestURL() | 获取客户端发出请求时的完整URL |
String getContextPath() | 获取web应用程序的路径,详情查看ServletContext那一部分 |
String getQueryString() | 获取请求行中的参数部分,也就是资源路径后面问号(?)以后的内容 |
String getServerName() | 获取当前主机名,可能是一个主机名或者是一个IP地址 |
实例:
String uri = req.getRequestURI();
StringBuffer url = req.getRequestURL();
String contextPath = req.getContextPath();
String queryString = req.getQueryString();
System.out.println(uri);
System.out.println(url);
System.out.println(contextPath);//输出空串
System.out.println(queryString);
我这里的请求发起的路径是localhost:8080/
,去访问localhost:8080/h/h?pid=001
,结果如下:
/h/h
http://localhost:8080/h/h
pid=001
获取请求头常用方法
方法声明 | 功能描述 |
---|---|
String getHeader(String name) | 获取一个指定头字段的值,如果请求消息头中没有指定的头字段,返回null |
Enumeration getHeaderNames() | 返回请求头字段的所有name |
实例:
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
String value = req.getHeader(header);
System.out.println(header + ":" + value);
}
//output
host:localhost:8080
connection:keep-alive
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36
sec-fetch-dest:document
利用Refer字段防盗链
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
String refer = req.getHeader("referer");
String site = "http://" + req.getServerName();
if (refer != null && refer.startsWith(site)) {
// 请求来自于本网站
} else {
// 请求来自其他网站
}
}
获取请求体的内容
方法声明 | 功能描述 |
---|---|
String getParameter(String name) | 获取请求参数中指定名称的参数值,无则返回NULL,若有多个则返回第一个出现的值。 |
String[] getParameterValues(String name) | 获取一个参数有多个值的情况,比如复选框。 |
Enumeration getParameteNames() | 返回所有请求消息中的参数名 |
Map getParameterMap() | 将请求消息中的参数名和值装进一个Map对象中返回 |
示例:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Html的Form表单元素</title>
</head>
<fieldset style="width:500px;">
<legend>Html的Form表单元素</legend>
<!--form表单的action属性规定当提交表单时,向何处发送表单数据,method属性指明表单的提交方式,分为get和post,默认为get-->
<form action="${pageContext.request.contextPath}/h/h" method="post">
<!--输入文本框,SIZE表示显示长度,maxlength表示最多输入长度-->
编 号(文本框):
<input type="text" name="userid" value="NO." size="2" maxlength="2"><br>
<!--输入文本框,通过value指定其显示的默认值-->
用户名(文本框):<input type="text" name="username" value="请输入用户名"><br>
<!--密码框,其中所有输入的内容都以密文的形式显示-->
密 码(密码框):
<!-- 表示的是一个空格-->
<input type="password" name="password" value="请输入密码"><br>
<!--单选按钮,通过checked指定默认选中,名称必须一样,其中value为真正需要的内容-->
性 别(单选框):
<input type="radio" name="sex" value="男" checked>男
<input type="radio" name="sex" value="女">女<br>
<!--下拉列表框,通过<option>元素指定下拉的选项-->
部 门(下拉框):
<select name="dept">
<option value="技术部">技术部</option>
<option value="销售部" SELECTED>销售部</option>
<option value="财务部">财务部</option>
</select><br>
<!--复选框,可以同时选择多个选项,名称必须一样,其中value为真正需要的内容-->
兴 趣(复选框):
<input type="checkbox" name="inst" value="唱歌">唱歌
<input type="checkbox" name="inst" value="游泳">游泳
<input type="checkbox" name="inst" value="跳舞">跳舞
<input type="checkbox" name="inst" value="编程" checked>编程
<input type="checkbox" name="inst" value="上网">上网
<br>
<!--大文本输入框,宽度为34列,高度为5行-->
说 明(文本域):
<textarea name="note" cols="34" rows="5">
</textarea>
<br>
<!--隐藏域,在页面上无法看到,专门用来传递参数或者保存参数-->
<input type="hidden" name="hiddenField" value="hiddenvalue"/>
<!--提交表单按钮,当点击提交后,所有填写的表单内容都会被传输到服务器端-->
<input type="submit" value="提交(提交按钮)">
<!--重置表单按钮,当点击重置后,所有表单恢复原始显示内容-->
<input type="reset" value="重置(重置按钮)">
</form>
<!--表单结束-->
</fieldset>
</body>
<!--完结标记-->
</html>
<!--完结标记-->
1、type常用属性:placeholder、password、radio、checkbox
2、复选框和单选框的name值要一致,这样才能被合理地查找,复选框和单选框value的值不会显示出来,但是要设置,主要是在后台需要取到name对应的value值。
3、对于复选框要使用getParameterValues(String name)
获取值,如果使用getParameter(String name)
获取多个值只会取到第一个。
通过getParameter和getParameterValues获取参数
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
req.setCharacterEncoding("UTF-8");
/**
* 编 号(文本框):
<input type="text" name="userid" value="NO." size="2" maxlength="2">
*/
String userid = req.getParameter("userid");//获取填写的编号,userid是文本框的名字,<input type="text" name="userid">
/**
* 用户名(文本框):<input type="text" name="username" value="请输入用户名">
*/
String username = req.getParameter("username");//获取填写的用户名
/**
* 密 码(密码框):<input type="password" name="userpass" value="请输入密码">
*/
String userpass = req.getParameter("password");//获取填写的密码
String sex = req.getParameter("sex");//获取选中的性别
String dept = req.getParameter("dept");//获取选中的部门
//获取选中的兴趣,因为可以选中多个值,所以获取到的值是一个字符串数组,因此需要使用getParameterValues方法来获取
String[] insts = req.getParameterValues("inst");
String note = req.getParameter("note");//获取填写的说明信息
String hiddenField = req.getParameter("hiddenField");//获取隐藏域的内容
String instStr="";
/**
* 获取数组数据的技巧,可以避免insts数组为null时引发的空指针异常错误!
*/
for (int i = 0; insts!=null && i < insts.length; i++) {
if (i == insts.length-1) {
instStr+=insts[i];
}else {
instStr+=insts[i]+",";
}
}
}
通过getParameterNames方法接收表单参数,这种情况下,多选框的多个值只能取到一个
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
req.setCharacterEncoding("utf-8");
Enumeration<String> paramNames = req.getParameterNames();//获取所有的参数名
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement();//得到参数名
String value = req.getParameter(name);//通过参数名获取对应的值
System.out.println(MessageFormat.format("{0}={1}", name, value));
}
}
使用getParameterMap方法接收表单参数
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
req.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = req.getParameterMap();
for (String s : parameterMap.keySet()) {
String[] values = parameterMap.get(s);
for (String value : values) {
System.out.println(s + ":" + value);
}
}
}
表单信息中文乱码
1)POST方式,设置req.setCharacterEncoding("utf-8");
解决乱码问题
2)GET方式提交表单,乱码问题,只能将以默认编码(ISO8859-1)从新解码在编码;对于地址栏上面的普通GET请求,只需要同POST设置就可以解决乱码。
利用Request对象传递数据
void setAttribute(String name, Object object):将一个对象与一个名称关联后放入ServletRequest对象中。如果对象中已经存在指定名称的属性,则会覆盖覆盖原来的属性值。如果传递给名称对象的值为NULL,则会删除指定名称的属性,相当于调用removeAttribute()方法。
String getAttribute(String name) :从ServletRequest对象中返回指定名称的属性对象。
void removeAttribute(String name):移除指定名称的属性。
Enumeration getAttributeNames():返回一个包含ServletRequest对象中的所有属性名Enumberation对象。
请求转发
RequestDispatcher接口包含两个方法:👁 📖 看注释
public class RequestDispatcher{
/**
* Forwards a request from a servlet to another resource (servlet, JSP file,
* or HTML file) on the server. This method allows one servlet to do
* preliminary processing of a request and another resource to generate the
* response.
*
* <p>
* For a <code>RequestDispatcher</code> obtained via
* <code>getRequestDispatcher()</code>, the <code>ServletRequest</code>
* object has its path elements and parameters adjusted to match the path of
* the target resource.
*
* <p>
* <code>forward</code> should be called before the response has been
* committed to the client (before response body output has been flushed).
* If the response already has been committed, this method throws an
* <code>IllegalStateException</code>. Uncommitted output in the response
* buffer is automatically cleared before the forward.
*/
public void forward(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
/**
* Includes the content of a resource (servlet, JSP page, HTML file) in the
* response. In essence, this method enables programmatic server-side
* includes.
*
* <p>
* The {@link ServletResponse} object has its path elements and parameters
* remain unchanged from the caller's. The included servlet cannot change
* the response status code or set headers; any attempt to make a change is
* ignored.
*
* <p>
* The request and response parameters must be either the same objects as
* were passed to the calling servlet's service method or be subclasses of
* the {@link ServletRequestWrapper} or {@link ServletResponseWrapper}
* classes that wrap them.
*/
public void include(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
}