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("哈哈");
    }
}

结论

  1. 输出中文建议使用字符流
  2. 解决字符流乱码 使用setContentType放可以同时设置编码解码两端的码表.
  3. 注意: 码表的设置一定放到输出之前
  4. 注意: 字符流与字节流不能同时使用

向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);这句之后,一般不在其后写代码了,通常这句是最后一行。

重定向和转发的区别

  1. 分别是发送了几次请求

    • 重定向:两次
    • 转发:一次
  2. 能不能访问项目之外的地址

  • 重定向:可以
  • 转发:不可以(转发是内部的事)
  1. 能不能使用request域共享数据
  • 重定向:不可以
  • 转发:可以
  1. 地址栏会不会发生变化
  • 重定向: 一般会
  • 转发:不会
  1. 请求方式是否会发生改变

    • 重定向: 有可能,重定向的第二个请求一定是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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 这部分主要是与Java Web和Web Service相关的面试题。 96、阐述Servlet和CGI的区别? 答...
    杂货铺老板阅读 1,401评论 0 10
  • 1.什么是JSP (1)jsp全称是Java Server Pages,它和Servlet技术一样都是sun公司定...
    yjaal阅读 3,644评论 5 99
  • 【学号】2017101205 【姓名】李嘉慧 【性别】女 【城市】山西省忻州市定襄县 【简书号】李嘉慧应数2班 【...
    李嘉慧应数2班阅读 284评论 1 0
  • 本文的感悟来自“李少加”的《职场沟通:基于价值对等的沟通》 一、有效沟通的基础:价值对等 沟通在职场中必不可少,尤...
    仰望星空_882c阅读 752评论 0 1