请求响应对象(JavaWeb片段四)

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:通常意义上的下载,弹出下载提示框

ContentType到底该怎么设置?

验证码

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/

细节

getOutputStreamgetWriter方法分别用于得到输出二进制数据、输出文本数据ServletOuputStream、Printwriter对象。
getOutputStreamgetWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 Servlet程序向ServletOutputStreamPrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
Serlvet的service方法结束后,Servlet引擎将检查getWritergetOutputStream方法返回的输出流对象是否已经调用过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");
    /**
     * 编&nbsp;&nbsp;号(文本框):
     <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");//获取填写的用户名
    /**
     * 密&nbsp;&nbsp;码(密码框):<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值