【JavaWeb】請求和響應Request&Response


請求

請求對象

關於請求

顧名思義,意思就是請求一個“對象”
請求不到的,別想了
請求,就是使用者希望從服務器端索取一些資源,向服務器發出詢問。在B/S架構中,就是客戶瀏覽器向服務器發出詢問。在JavaEE工程中,客戶瀏覽器發出詢問,要遵循HTTP協議規定。

請求對象,就是在JavaEE工程中,用於發送請求的對象。我們常用的對象就是ServletRequest和HttpServletRequest,它們的區別就是是否和HTTP協議有關。

常用請求對象

image

常用請求方法

image

請求對象的使用示例

常用方法一:請求各種路徑

/*
    獲取路徑的相關方法
 */
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取虛擬目錄名稱 getContextPath()
        String contextPath = req.getContextPath();
        System.out.println(contextPath);

        //2.獲取Servlet映射路徑 getServletPath()
        String servletPath = req.getServletPath();
        System.out.println(servletPath);

        //3.獲取訪問者ip getRemoteAddr()
        String ip = req.getRemoteAddr();
        System.out.println(ip);

        //4.獲取請求消息的數據 getQueryString()
        String queryString = req.getQueryString();
        System.out.println(queryString);

        //5.獲取統一資源標識符 getRequestURI()    /request/servletDemo01   
        String requestURI = req.getRequestURI();
        System.out.println(requestURI);

        //6.獲取統一資源定位符 getRequestURL()    http://localhost:8080/request/servletDemo01  
        StringBuffer requestURL = req.getRequestURL();
        System.out.println(requestURL);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

常用方法二:獲取請求參數以及封裝(非常重要)

我們常常會使用HttpServletRequest對象獲取請求參數,然后將其封裝到實體類中
image

/*
    獲取請求參數信息的相關方法
 */
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.根據名稱獲取數據   getParameter()
        String username = req.getParameter("username");
        System.out.println(username);
        String password = req.getParameter("password");
        System.out.println(password);
        System.out.println("--------------------");

        //2.根據名稱獲取所有數據 getParameterValues()
        String[] hobbies = req.getParameterValues("hobby");
        for(String hobby : hobbies) {
            System.out.println(hobby);
        }
        System.out.println("--------------------");

        //3.獲取所有名稱  getParameterNames()
        Enumeration<String> names = req.getParameterNames();
        while(names.hasMoreElements()) {
            String name = names.nextElement();
            System.out.println(name);
        }
        System.out.println("--------------------");

        //4.獲取所有參數的鍵值對 getParameterMap()
        Map<String, String[]> map = req.getParameterMap();
        for(String key : map.keySet()) {
            String[] values = map.get(key);
            System.out.print(key + ":");
            for(String value : values) {
                System.out.print(value + " ");
            }
            System.out.println();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

下面通過實例說明幾種封裝方式

需求:我們要實現從網頁填寫學生注冊信息,然后把獲取請求參數並把相應的信息封裝到每一個Student類中。

第一步:編寫一個頁面html程序
image


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注冊頁面</title>
</head>
<body>
    <form action="/request/servletDemo08" method="post" autocomplete="off">
        姓名:<input type="text" name="username"> <br>
        密碼:<input type="password" name="password"> <br>
        愛好:<input type="checkbox" name="hobby" value="study">學習
              <input type="checkbox" name="hobby" value="game">游戲 <br>
        <button type="submit">注冊</button>
    </form>
</body>
</html>

第二步:編寫Student的javabean類,注意其數據成員最好(必須)與html文件表單的name屬性一致

public class Student {
    private String username;
    private String password;
    private String[] hobby;

    public Student() {
    }

    public Student(String username, String password, String[] hobby) {
        this.username = username;
        this.password = password;
        this.hobby = hobby;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String[] getHobby() {
        return hobby;
    }

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }

    @Override
    public String toString() {
        return "Student{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", hobby=" + Arrays.toString(hobby) +
                '}';
    }
}

第三步:獲取參數信息,並封裝數據

法一:直接手動封裝(簡單粗暴)

/*
    封裝對象-手動方式
 */
@WebServlet("/servletDemo04")
public class ServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取所有的數據
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbies = req.getParameterValues("hobby");

        //2.封裝學生對象
        Student stu = new Student(username,password,hobbies);

        //3.輸出對象
        System.out.println(stu);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

法二:通過反射封裝
PropertyDescriptor: Describes a Java Bean property hosting validation constraints
調用javabean類的有參構造函數創建對象
構造函數 PropertyDescriptor(E,class); 注意第一個參數是javabean構造函數的第一個形式參數,第二個參數是已經創建的實類的字節碼。

/*
    封裝對象-反射方式
 */
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取所有的數據
        Map<String, String[]> map = req.getParameterMap();

        //2.封裝學生對象
        Student stu = new Student();
        //2.1遍歷集合
        for(String name : map.keySet()) {
            String[] value = map.get(name);
            try {
                //2.2獲取Student對象的屬性描述器
                PropertyDescriptor pd = new PropertyDescriptor(name,stu.getClass());
                //2.3獲取對應的setXxx方法
                Method writeMethod = pd.getWriteMethod();
                //2.4執行方法
                if(value.length > 1) {
                    writeMethod.invoke(stu,(Object)value);
                }else {
                    writeMethod.invoke(stu,value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //3.輸出對象
        System.out.println(stu);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

法三:BeanUtils工具類封裝
BeanUtils.populate(stu,map);(實類,參數map)

/*
    封裝對象-工具類方式
 */
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取所有的數據
        Map<String, String[]> map = req.getParameterMap();

        //2.封裝學生對象
        Student stu = new Student();
        try {
            BeanUtils.populate(stu,map);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //3.輸出對象
        System.out.println(stu);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

用流的形式讀取請求信息

/*
    流對象獲取數據
 */
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //字符流(必須是post方式)
        /*BufferedReader br = req.getReader();
        String line;
        while((line = br.readLine()) != null) {
            System.out.println(line);
        }*/
        //br.close();

        //字節流
        ServletInputStream is = req.getInputStream();
        byte[] arr = new byte[1024];
        int len;
        while((len = is.read(arr)) != -1) {
            System.out.println(new String(arr,0,len));
        }
        //is.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

請求正文中中文編碼問題

1.POST方式請求

問題:獲取請求正文,會有亂碼問題。是在獲取的時候就已經亂碼了。

解決:是request對象的編碼出問題了。設置request對象的字符集
request.setCharacterEncoding("編碼方式")它只能解決POST的請求方式,GET方式解決不了

/*
    中文亂碼
 */
@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //設置編碼格式
        req.setCharacterEncoding("UTF-8");
        String username = req.getParameter("username");
        System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

2.GET方式請求

問題:GET方式:正文在地址欄username=%D5%C5%C8%FD%D5%C5%C8%FD是已經被編過一次碼了

GET方式請求的正文是在地址欄中,在Tomcat8.5版本及以后,Tomcat服務器已經幫我們解決了,所以不會有亂碼問題了。

而如果我們使用的不是Tomcat服務器,或者Tomcat的版本是8.5以前,那么GET方式仍然會有亂碼問題,解決方式如下:

使用正確的碼表對已經編過碼的數據進行解碼。就是把取出的內容轉成一個字節數組,但是要使用正確的碼表。(ISO-8859-1)再使用正確的碼表進行編碼,把字節數組再轉成一個字符串,需要使用正確的碼表,是看瀏覽器當時用的是什么碼表。

/**
 * 在Servlet的doGet方法中添加如下代碼
 */
public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String username = request.getParameter("username");
        byte[] by = username.getBytes("ISO-8859-1");
        username = new String(by,"GBK");

        //輸出到瀏覽器:注意響應的亂碼問題已經解決了
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.write(username);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    doGet(request, response);
}

請求轉發(與重定向的區別)

重定向特點:兩次請求,瀏覽器行為,地址欄改變,請求域中的數據會丟失

請求轉發:一次請求,服務器行為,地址欄不變,請求域中的數據不丟失

請求域的作用范圍:當前請求(一次請求),和當前請求的轉發之中

請求發送方:

/*
    請求轉發
 */
@WebServlet("/servletDemo09")
public class ServletDemo09 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //設置共享數據
        req.setAttribute("encoding","gbk");

        //獲取請求調度對象
        RequestDispatcher rd = req.getRequestDispatcher("/servletDemo10");
        //實現轉發功能
        rd.forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

請求接收方:

/*
    請求轉發
 */
@WebServlet("/servletDemo10")
public class ServletDemo10 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取共享數據
        Object encoding = req.getAttribute("encoding");
        System.out.println(encoding);

        System.out.println("servletDemo10執行了...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

開啟服務器后進入/servletDemo09之后會在控制台輸出

encoding
servletDemo10執行了...

而此時瀏覽器的url依然是/servletDemo09,不會跳轉

請求重定向

請求重定向:客戶端的一次請求到達后,發現需要借助其他Servlet來實現功能
特點:瀏覽器地址欄會發生改變,兩次請求、請求域對象中不能共享數據,可以重定向到其他服務器

void sendRedirect(String name) 設置重定向

請求發送方:

/*
    請求重定向
 */
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //設置請求域數據
        req.setAttribute("username","zhangsan");

        //設置重定向
        resp.sendRedirect(req.getContextPath() + "/servletDemo07");
		
		// resp.sendRedirect("https://www.baidu.com");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

請求接收方:

/*
    請求重定向
 */
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo07執行了...");
        Object username = req.getAttribute("username");
        System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

輸出:
servletDemo07執行了...
null

因為請求了兩次,請求域中的數據丟失,所以數據不共享。
查看客戶端瀏覽器發現servletDemo06的Status Code是302 表示請求重定向
而servletDemo07的Status Code是202 表示正常。

請求包含

需求:把兩個Servlet的內容合並到一起來響應瀏覽器
問題:HTTP協議的特點是一請求,一響應的方式。所以絕對不可能出現有兩個Servlet同時響應方式。
解決:把兩個Servlet的響應內容合並輸出。

/*
    請求包含
 */
@WebServlet("/servletDemo11")
public class ServletDemo11 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo11執行了...");

        //獲取請求調度對象
        RequestDispatcher rd = req.getRequestDispatcher("/servletDemo12");
        //實現包含功能
        rd.include(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
/*
    請求包含
 */
@WebServlet("/servletDemo12")
public class ServletDemo12 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo12執行了...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

控制台輸出
servletDemo11執行了...
servletDemo12執行了...
而且瀏覽器的url依然是/servletDemo11,不會跳轉

細節

請求轉發的注意事項:負責轉發的Servlet,轉發前后的響應正文丟失,由轉發目的地來響應瀏覽器。

請求包含的注意事項:被包含者的響應消息頭丟失。因為它被包含起來了。

響應

響應對象

關於響應

服務器端收到請求,同時也已經處理完成,把處理的結果告知用戶。
在B/S架構中,響應就是把結果帶回瀏覽器。

常用響應對象

協議無關的對象標准是:ServletResponse接口

協議相關的對象標准是:HttpServletResponse接口

image

常用方法介紹

image

常用狀態碼:

狀態碼 說明
200 執行成功
302 它和307一樣,都是用於重定向的狀態碼。只是307目前已不再使用
304 請求資源未改變,使用緩存。
400 請求錯誤。最常見的就是請求參數有問題
404 請求資源未找到
405 請求方式不被支持
500 服務器運行內部錯誤

狀態碼首位含義:

狀態碼 說明
1xx 消息
2xx 成功
3xx 重定向
4xx 客戶端錯誤
5xx 服務器錯誤

響應對象的使用示例

字節流輸出中文問題

項目中常用的編碼格式是u8,而瀏覽器默認使用的編碼是gbk。導致亂碼!

解決方式一:修改瀏覽器的編碼格式(不推薦,不能讓用戶做修改的動作)
解決方式二:通過輸出流寫出一個標簽:response.getOutputStream().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>")
解決方式三:response.setHeader("Content-Type","text/html;charset=UTF-8"); 指定響應頭信息
解決方式四:response.setContentType("text/html;charset=UTF-8"); (常用)

/*
    字節流響應消息及亂碼的解決
 */
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String str = "你好";

        resp.setContentType("text/html;charset=UTF-8");

        sos.write(str.getBytes("UTF-8"));
        sos.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

字符流輸出中文問題

/*
    字符流響應消息及亂碼的解決
 */
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String str = "你好";

        //解決中文亂碼
        resp.setContentType("text/html;charset=UTF-8");

        //獲取字符流對象
        PrintWriter pw = resp.getWriter();
        //pw.println(str);
        pw.write(str);
        pw.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

響應圖片到瀏覽器

/*
    響應圖片到瀏覽器
 */
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通過文件的相對路徑來獲取文件的絕對路徑
        String realPath = getServletContext().getRealPath("/img/hm.png");
        System.out.println(realPath);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

        //獲取字節輸出流對象
        ServletOutputStream sos = resp.getOutputStream();

        //循環讀寫
        byte[] arr = new byte[1024];
        int len;
        while((len = bis.read(arr)) != -1) {
            sos.write(arr,0,len);
        }

        bis.close();
        sos.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

控制緩存

resp.setDateHeader("Expires",(System.currentTimeMillis()+時間));

/*
    緩存
 */
@WebServlet("/servletDemo04")
public class ServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String news = "這是一條很火爆的新聞~~";

        //設置緩存時間
        resp.setDateHeader("Expires",(System.currentTimeMillis()+1*60*60*1000L));

        //設置編碼格式
        resp.setContentType("text/html;charset=UTF-8");
        //寫出數據
        resp.getWriter().write(news);
        System.out.println("aaa");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

定時刷新

resp.setHeader("Refresh","定時時間(秒);URL=/虛擬路徑/頁面路徑");

/*
    定時刷新
 */
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String news = "您的用戶名或密碼錯誤,3秒后自動跳轉到登錄頁面...";

        //設置編碼格式
        resp.setContentType("text/html;charset=UTF-8");
        //寫出數據
        resp.getWriter().write(news);

        //設置響應消息頭定時刷新
        resp.setHeader("Refresh","3;URL=/response/login.html");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

文件下載

/*
    文件下載
 */
@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.創建字節輸入流,關聯讀取的文件
        //獲取文件的絕對路徑
        String realPath = getServletContext().getRealPath("/img/hm.png");
        //創建字節輸出流對象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

        //2.設置響應頭支持的類型  應用支持的類型為字節流
        /*
            Content-Type 消息頭名稱   支持的類型
            application/octet-stream   消息頭參數  應用類型為字節流
         */
        resp.setHeader("Content-Type","application/octet-stream");

        //3.設置響應頭以下載方式打開  以附件形式處理內容
        /*
            Content-Disposition  消息頭名稱  處理的形式
            attachment;filename=  消息頭參數  附件形式進行處理
         */
        resp.setHeader("Content-Disposition","attachment;filename=" + System.currentTimeMillis() + ".png");

        //4.獲取字節輸出流對象
        ServletOutputStream sos = resp.getOutputStream();

        //5.循環讀寫文件
        byte[] arr = new byte[1024];
        int len;
        while((len = bis.read(arr)) != -1) {
            sos.write(arr,0,len);
        }

        //6.釋放資源
        bis.close();
        sos.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM