網站高級

2.1    前端界面庫
常見的前端界面庫:layui,bootstrap等
本系統教程採用的是layui作爲教學
前端界面庫包含什麼:
-JS    -CSS    -少許界面圖片資源
爲何要使用前端界面庫
-統一風格    -快速構建前端界面
可是,對於大型前端開發團隊,通常會本身定義.

2.2    使用layui
解壓layui,把layui目錄拷貝到項目下
能夠發現1,裏面有js,css,字體,圖片資源
在HTML中引入
在HTML頭部加上:
layui/layui.all.js
layui/css/layui.css
第一個例子
在HTML中引入layui裏的樣式.
<button class="layui-btn">一個標準的按鈕</button>

2.3    使用表格
參照https://www.layui.com/doc/element/table.html
官網文檔
    <body>
        <table class="layui-table">
            <colgroup>
                <col width="150">
                <col width="200">
                <col>
            </colgroup>
            <thead>
                <tr>
                    <th>暱稱</th>
                    <th>加入時間</th>
                    <th>簽名</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>賢心</td>
                    <td>2016-11-29</td>
                    <td>人生就像是一場修行</td>
                </tr>
                <tr>
                    <td>許閒心</td>
                    <td>2016-11-28</td>
                    <td>於千萬人之中碰見你所碰見的人</td>
                </tr>
            </tbody>
        </table>

    </body>

2.4    表單

本節課介紹 layui 裏面的表單的使用

1 添加表單
給 div 添加 class='layui-form' 樣式
<div id='info' class="layui-form" style='width: 600px;margin:20px;'>
    ... 表單 ...
</div>

2 添加一行
添加一個div, 指定class='layui-form-item' , 表示一行
            <div class="layui-form-item">
                .... 表單的一行 ...
            </div>

3 添加行內控件
通常會在行內添加一個label, input
例如,左側label, 右側 input 
                <label class="layui-form-label">職位</label>
                <div class="layui-input-block">
                    <input class="layui-input" name='title'>
                </div>
在這裏,用 layui-input-block 將<input> 包起來使用
layui-input-block: 佔剩餘空間
layui-input-inline : 指定空間
 
4 select / checkbox / ratio
在 layui 裏,下拉列表要顯示出效果來,必須手工調用 JS 
在scrpit裏添加一行:
layui.form.render();    //對select/checkbox/ratio自動渲染
只有運行了這一行代碼,才能顯示出正常效果

5 layui 與 jquery
據做者說,layui裏自帶了一個1.x版本的jquery。不過,咱們還用本身的jquery吧。
順序: 先引入jquery,再引入layui 。
        <script type="text/javascript" src="js/jquery.min.js" ></script>
        <script type="text/javascript" src="layui/layui.all.js"></script>
        <link rel="stylesheet" href="layui/css/layui.css" />
而後即可以按正常方式來使用jquery了。

2.5    提示框
layui.layer.msg():提示消息,自動關閉
layui.layer.alert():提示消息
layui.layer.confirm():確認輸入
.msg的提示消息自動關閉
<body>
    <button class="layui-btn layui-btn-normal" onclick='test()'>消息提示</button>
</body>
<script>
    function test()
    {
        layui.layer.msg('welcome,boy!');
        //能夠附加參數
        //layui.layer.msg('welcome,boy!',(time:1500));
    }
</script>

.alert提示消息
    <body>
        <button class="layui-btn layui-btn-normal" onclick='test()'>消息提示</button
    </body>
    <script>
        function test()
        {
            layui.layer.alert('xxxx',{
                title:false,
                btn:['肯定','取消'],
                yes:function(index,layero){
                    console.log('執行刪除操做...');
                    layui.layer.close(index);
                },
                cancel:function(index,layer){
                console.log('取消了操做...');
                }
            });
            //能夠附加一些參數
            //layui.layer.msg('welcome,boy!',{time:1500});
        }
    </script>

使用open方法能夠實現前面所有的功能,實質上.alert和.msg就是open方法的一種封裝

2.6    彈出框
幾個layui通用彈出式對話框的用法,
打開對話框
var index=layui.layer.open(...);
關閉對話框
layui.layer.close(index);
注:layui裏用一個數字index來表明對話框
彈出框
(1)index:一個數字,指代對話框
(2)layero:此參數指向建立的DOM層對象
(3)success:此回調在建立層並顯示後調用
(4)yes:此回調在點確認按鈕後調用.
注:layui會新建一個層<div>
    <body>
        
        <button class="layui-btn layui-btn-normal" onclick='test()'> 購買 </button>
        
        <!-- 請定義一個頂級div做爲對話框的內容 -->
        <div id='buy_dialog_content' style='display:none'>
            <div style='padding: 20px'>
                <div>
                    <label class='layui-form-label'> 數量</label>                    
                    <div class="layui-input-inline">
                        <input class='layui-input' style='width:140px' name='amount' >
                    </div>                    
                </div>
            </div>            
        </div>
    </body>
    
    <script>
        
        function test()
        {
            //返回值時一個數字,在layui裏叫index,用來指代一個對話框
            //title:標題
            //content:內容(請單獨定義一個頂級的<div>作爲內容)
            //id:指定layui生成的層對象的id
            //area:寬高
            //btn:按鈕
            //success:在層生成顯示後的回調方法,能夠在裏面初始化表單的值
            //yes:點確認按鈕的回調

            var layerIndex = layui.layer.open({
                title: "購買數量設定",
                content: $('#buy_dialog_content').html(),
                id : 'buy_dialog',
                area:['400px', 'auto'],
                btn: ['肯定'],
                success: function(layero, index){
                    console.log('初始化顯示...');
                    $('[name=amount]', layero).val(1);
                },
                yes: function(index,layero){
                    var amount = $('[name=amount]', layero).val();
                    console.log('amount=' + amount);
                    layui.layer.close(index);
                }
            });
            
            // 能夠附加一些參數
            // layui.layer.msg('welcome , boy !', { time: 1500 });
        }
        
    </script>

2.7    獨立的layer模塊
layer.layui.com
在項目中能夠只使用layer的包,而不使用layui.
layui就是給後端使用的簡單的前端框架.
獨立的layer模塊
1.先引jquery.js,再引layer.js
2.使用layer.open()建立層.而不是使用layui.layer.open().
3.不含layui的其餘模塊和樣式

3.1    富文本編輯器
普通富文本編輯器:<textarea>,只能編輯純文本
富文本編輯器:帶格式的能圖文混編的編輯器
常見的有,
UEditor:https://ueditor.baidu.com
KingEditor:http://kindeditor.net/

富文本編輯器特色
-基於HTML網頁實現    -支持豐富的格式,如顏色,字體,對齊
-支持圖文混編        -支持JS的API操做

3.2    KindEditor
演示版本:4.1.11
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        
        <script charset="utf-8" src="kindeditor/kindeditor-all-min.js"></script>
        <script charset="utf-8" src="kindeditor/lang/zh-CN.js"></script>    //中文支持包
        
    </head>
    <body>
        <!-- kindeditor placehoder -->
        <textarea id="editor" style="width:700px;height:300px;"></textarea>
    </body>
    
    <script>        
        var editor = KindEditor.create('#editor');
        //editor.html("<p> This is KindEditor. </p>");        
    </script>
</html>
使用富文本編輯器KindEditor.

3.3    初始化參數
在初始化KindEditor時,能夠指定一些配置參數
var editor=KindEditor.create('#editor',{
...參數...
});
好比,
items:工具按鈕組
cssData:文本樣式
    <script>        
        var config = {
            cssData : 'p{ font-size:14px;text-indent:2em;}' ,
            items : [ 'source', 
                , '|','fontname', 'fontsize'
                , '|', 'forecolor', 'hilitecolor', 'bold','italic', 'underline', 'strikethrough', 'lineheight'
                , '|', 'justifyleft', 'justifycenter', 'justifyright'
            ]    //items定義工具按鈕
        };
        
        var editor = KindEditor.create('#editor', config);
        //editor.html("<p> This is KindEditor. </p>");        
    </script>

3.4    API
KindEditor提供一個API以便操做
如,editor.html()能夠設置/取值
editor.insertHtml()能夠在當前位置插值
注:能夠引入jquery,和jquery同時使用.
        <button onclick='do_submit()'>發佈</button>

        function do_submit()
        {
            var data={};
            data.title=$('[name=title]').val();
            data.content=editor.html();
            console.log(data);
        }

3.5    圖片上傳    //用到時再看.
在 MyEclipse 裏新建一個 Web 項目, 部署到 tomcat 下
http://127.0.0.1:8080/senior0305/


1 在工具欄添加一個圖片按鈕
    items : [ 'source', , '|','fontname', 'fontsize','image', '|', 'forecolor', 'hilitecolor', 'bold','italic',     ]

2 後臺添加一個接收文件上傳的 Servlet
KindEditorUpload
UrlPattern:  *.kind
按照 kindeditor 的要求,上傳成功時返回(示例),
    {
        "error" : 0,
        "url" : "http://www.example.com/path/to/file.ext"
    }
上傳失敗時返回(示例),
    {
        "error" : 1,
        "message" : "錯誤信息"
    }

3 在 kindeditor 初始化參數裏設定,
            uploadJson : 'fileupload.kind' 
即指定後臺服務的地址

4.1    Hibernate表的設計
例如    學生信息表student
字段    類型    意義
id    INT    行ID
code    STRING    編號(學號)
name    STRING    姓名
gender    TINYINT    性別
deptId    SMALL INT 院系ID
year    SMALL INT 入院年份
contact STRING    聯繫電話
email    STRING     郵箱
timeCreated DATETIME    建立時間
timeModified DATATIME    修改時間
命名規範
表名:小寫,加下劃線
如user_log user_privilege
列名:首單詞小寫,其餘大寫首字母
如:timeCreated        lastLoginTime

自增主鍵,索引,自增起點
id:自增主鍵(數字ID查詢速度快)
code:學號,字符串型 爲學號創建unique index    unique惟一索引
自增起點:1000000(100萬爲起點,目的ID整齊,都是七位整數)

其餘
bool類型等價於tinyint(1)
varchar列的最大長度,影響其實不大

再創建一張院系的表dept
字段        類型        意義
id        SMALLINT    院系ID    
code        STRING        院系編號
name        STRING        名稱
manager        STRING        負責人名稱
telephone    STRING        聯繫電話
timeCreated    DATETIME    建立時間
timeModified    DATETIME    修改時間

表的關聯關係
在student表中,deptId記錄的是該學生所在院系的ID
student.deptId<--------->dept.id

數量級估計(重要)
學生:每一年10000*10年,最大100000人(10萬)
數量每一年增長一次(INT)
院系:數量<500,幾乎不會變化(SMALL INT)

自增ID<-->指定ID
student.id:自增
院系ID:因爲院系比較少,因此能夠手工指定ID,也能夠自增
小結:設計了兩張表student和dept.

4.2    Hibernate 生成POJO類
逆向生成POJO類
使用Hibernate Reverse Engineering

8.1    初識HTTP
HTTP協議
HTTP,HyperText Transfer Protocol(基於TCP)
簡單的說,用於請求資源/下載文件的一個協議
服務器稱爲HTTP服務器,如tomcat
客戶端稱爲HTTP客戶端,如chrome瀏覽器

HTTP協議
從客戶端的視角來描述HTTP協議
好比,服務器上有一個文件,book.jpg
想要下載這個文件,因而
(1)創建tcp socket,鏈接到服務器
(2)發送請求,請求裏註明我要的文件
(3)接收請求,把數據保存到文件
演示
test.afanihao.cn/201705/hello.txt
test.afanihao.cn/201705/book.jpg
注:清理緩存,不然可能提示304Not Modified

8.2    HTTP請求(Socket)
HTTP的幾種實現方式
1.使用Socket(理解原理)
2.使用URLConnect
3.使用Apache Http Component(最多見的API)

HTTP:用Socket實現
HTTP是基於TCP協議,創建一個Socket便可實現
大體上就是一個普通TCP的基本使用步驟:
Socket s=new Socket()
s.connect(...)
s.getOutputStream().write(...)
s.getInputStream().read(...)
s.close()

HTTP的幾種實現方式
發送請求:
String request="GET/201802/book.jpg HTTP/1.1\r\n"
    +"HOST:test.afanihao.cn\r\n"
    +"\r\n";
注意:每行末尾\r\n

HTTP的幾種實現方式
接收應答:
InputStream instream=client.getInputStream();
解析部分稍微複雜,大體思路:
-先把頭部的每一行接收下來
-空行表示頭部結束
-繼續接收,把正文部分保存下來.

難點:
1.HTTP的頭部是文本形式,正文是字節形式
所以正文裏能夠是一個jpg文件的數據,也是一個mp4文件的數據.
2.空行標誌着頭部的結束,正文的開始
問題是:什麼叫空行
3.如何一行一行的接收
4.用BufferedReader來讀取頭部時,會多讀取到一部分正文數據.
所以用本身寫的AfBufferedReader

小結:
1.展現了Socket方式訪問HTTP的方法
2.經常使用的方式是使用Apache Http Component的API

 

8.3    HTTP請求:URLConnection
Java自帶的URLConnection

public class Test2
{

    public static void main(String[] args) throws Exception
    {
        URL url = new URL("http://test.afanihao.cn/201705/book.jpg");
        URLConnection conn= url.openConnection();
        
        // 能夠本身添加一些必要的 HTTP Header
        //conn.setRequestProperty("accept", "*/*");
        //conn.setRequestProperty("connection", "Keep-Alive");
        //conn.setRequestProperty("user-agent", "Mozilla/4.0(compatible;MSIE)");
        
        //////////////////  鏈接服務器 ///////////////
        conn.connect();
        
        ///////////////////  處理應答 //////////////// 
        InputStream instream = conn.getInputStream();
        
        // 獲取Content-Length, 也能夠直接獲取到其餘的HTTP Header
        String fieldValue = conn.getHeaderField("Content-Length");
        int contentLength = Integer.valueOf( fieldValue);
        
        // 讀取HTTP內容, 保存到文件
        File localfile = new File("c:/download2.jpg");
        FileOutputStream fstream = new FileOutputStream(localfile);
                
        // 而後繼續從socket讀取數據
        int sizeContent = 0; 
        byte[] buffer = new byte[4000];
        while( sizeContent < contentLength )
        {
            int n = instream.read(buffer);
            if(n <= 0) break;
            fstream.write(buffer, 0, n);
            sizeContent += n;
        }
        fstream.close();
        System.out.println("** 保存到: " + localfile + ", 共" + localfile.length() + "字節");    
        
        // 其餘清理工做 ...
    }

}

8.4    HTTP請求:HttpClient
最經常使用的API
http://hc.apache.org/
Http Component包含:
-HttpCore:服務器API,當前版本5.0
-HttpClient:客戶端API,當前版本4.5

public class Test3
{
    public static void doGet(String url, File localFile, int timeout) throws Exception
    {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpget = new HttpGet(url);
        CloseableHttpResponse response = httpclient.execute(httpget);// 發起HTTP
        
        // 設置超時時間
        if(timeout>0)
        {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(timeout)
                    .setConnectTimeout(timeout)
                    .build();        
            httpget.setConfig(requestConfig);        
        }
        
        try
        {
            // 檢查狀態碼
            StatusLine statusLine = response.getStatusLine();
            int status = statusLine.getStatusCode();
            if(status != 200)
            {
                String reason = statusLine.getReasonPhrase();
                throw new Exception("Status Error: " + status + "," + reason);
            }
            
            // 保存文件
            final int MAXSIZE = 1024*1024*1024; // 128M
            byte[] buf = new byte[1024*4];
            
            HttpEntity entity = response.getEntity();
            if (entity != null)
            {                
                InputStream instream = entity.getContent();
                FileOutputStream fstream = new FileOutputStream(localFile);
                
                int total = 0;
                int retry = 0;
                while( total < MAXSIZE)
                {
                    int n  = instream.read( buf);                    
                    if(n < 0) break;
                    if(n == 0)
                    {
                        // busy
                        try{ Thread.sleep(5);}catch(Exception e){}
                        
                        if(retry++ > 5) 
                        {
                            System.out.println("** 不能下載, 重試次數:" + retry);
                            break;
                        }
                        else continue; // 容許在服務器busy的狀況下重試N次
                    }                    
                
                    fstream.write(buf, 0, n);
                    retry = 0;
                    total += n;
                }
                
                System.out.println("** HTTP GET finished,  total: " + total);                
            }            
        } finally
        {
            try{response.close();}catch(Exception e){}            
        }    
    }

    
    public static void main(String[] args)
    {
        
        String url = "http://hc.apache.org/";
        File localFile = new File("d:/download3");
        try
        {
            doGet(url, localFile, 0);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        
        System.out.println("done");

    }

}

對比:URLConnection和HttpClient
HttpClient更強大,近似一個無UI的瀏覽器
-支持https    -支持zip傳輸,chunk傳輸
-支持客戶端Cookie    -支持multipart上傳

8.5    HTTP協議
HTTP交互    -短鏈接:1個請求,1個應答        -服務器不會主動找客戶端
HTTP請求格式:
第一行:Request Line
Method:GET/POST(僅僅是提示做用)
URI:資源地址
HTTP/1.1:協議版本號
請求字段:(以冒號空格分開)
Host:主機地址,域名加端口號
(注:一個主機能夠有多個域名的)
User-Agent:客戶端類型(操做系統+瀏覽器)
Connection:keep-alive,但願長鏈接;close,短鏈接(默認keep-alive)

HTTP應答格式
第一行:Status Line 示例"HTTP/1.1 200 OK"
Status:2xx,正常;4xx,請求錯誤;5xx,服務器內部錯誤
Reason:錯誤描述
404 Not Found
頭部字段:
Content-Type:內容類型,如text/plain
Content-Length:內容長度
Connection:keep-alive,但願長鏈接;close,短鏈接(默認keep-alive)

常見問題
1.GET/POST/PUT/DELETE
僅提示做用,不能說GET就一點是讀,POST就必定是寫.最終解釋權在服務器端,服務器想怎麼實現就怎麼實現

2.Connection:keep-alive
keep-alive,僅僅是但願對方別關鏈接:對方能夠關,也能夠不關.
另外,這裏的長鏈接,僅僅是不關閉socket,用於加快訪問速度.並不具有長鏈接的其餘特色.

8.6    Session和Cookie
權限問題
1.登陸:提交用戶名密碼到後臺
2.下載某某資源
客戶端登陸 sha123/123456---->服務器---->客戶端
客戶端  下載文件--->服務器--->客戶端

session權限問題
請求1:
>>>POST /login HTTP/1.1
username=shaofa&password=123456
<<<HTTP/1.1 200 OK
(建立一個session對象,而後session id返回)
請求2:
>>>GET/download/qinshi.MP4
sessionid
<<<解出sessionid,取出session對象,從session對象裏取出存入的username

Session
(1).第一次訪問時,服務器返回一個ID,稱爲JSESSIONID
(2).後面再訪問,客戶端要把JSESSIONID傳上來 這樣,後面每次再訪問時,
後臺檢查這個ID關聯的對象,就能知道這個客戶端當前的用戶信息了.
好比,後臺建立一個Map<String,Session>
在後臺,每一個JSESIONID關聯着一個Session對象.

解決下載權限問題:
第一個請求:POST/login
後臺校驗username/password以後,取出JESSIONID關聯的Session對象,把username存到Session裏.
request.getSession().setAttribute("username","shaofa");

第二個請求:GET/download/qinshi.mp4
後臺從JESSIONID,Session對象,取出當前用戶信息
String username=request.getSession().getAttribute("shaofa")

JsessionID在哪裏
第一次訪問時
>>>...
<<<Set-Cookie:JESSIONID=xxxxxx
後面訪問時
>>>Cookie:JSESSIONID=xxxxxx
<<<...
注:瀏覽器會自動把Set-Cookie的值保存下來,並在下次訪問時把Cookie值傳給服務器

最開始訪問時,客戶端沒有jsessionid,因此不發jsessionid到服務器...
服務器端給它建立一個session,而後set-cookie:jsessionid=xxxxx...客戶端存儲cookie...
客戶端繼續訪問該網站,每次訪問的時候,都把cookie:jsessionid=xxxxx...服務器經過jsessionid找到當前的客戶端對應的session對象.
session對象裏面能夠存儲用戶名,用戶權限.

小結:
1.服務器用JSESSIONID來關聯一個Session對象
2.客戶端用Cookie機制來存儲和上傳JSESSIONID

思考:
-每一個訪問都建立一個Session對象,那麼Session對象會愈來愈多?
-Cookie的值有幾種存儲機制,能夠存儲幾天,也能夠隨瀏覽器關閉而銷燬.
jsessionid的存儲期限是會話期,瀏覽器關閉了會自動銷燬.

9.1    HttpClient 下載文件
HttpComponent來自apache.org下的一個項目,包含服務器端和客戶端.
服務器端:HttpCore    客戶端:HttpClient
HttpClient很強大,近似一個無UI的瀏覽器
-支持https
-支持zip傳輸,chunk傳輸
-支持客戶端Cookie
-支持multipart上傳
使用HttpClient能夠下載一個文件,例如,下載一個圖片文件.
HttpClient,就是一個客戶端(相似於一個瀏覽器)
1.建立一個HttpClient對象
2.使用這個HttpClient對象,用它發起HTTP請求,並接收穫得服務器返回的數據.
3.發起更多HTTP請求.一個HttpClient能夠被重複使用.

注意事項:
1.HttpClient4.5內部已經出來了多種傳輸編碼方式,如zip,chunk
咱們從InputStream裏讀出來的,是解碼過的數據.
2.HttpClient本身可以處理HTTPS,加密解密和證書不須要咱們處理.
3.HttpClient對象是能夠重複使用的,並且線程安全的.
(多個線程使用同一個HttpClient對象是能夠的)

9.2    模擬表單上傳
表單<form>
HTML裏的表單,能夠被瀏覽器提交到服務器
<form method='post'
    action='servlet/Ex20170607FormReqeust'>
    <lable>學號</lable>
    <input class='m-input' name='id'/><br>
    <lable>姓名</lable>
    <input class='m-input' name='name'/><br>
    <input class='m-input' type='submit'value='提交'>
</form>
使用HttpClient,能夠把表單數據提供給後臺
這意味着,HttpClient和瀏覽器能夠共用一個後臺服務(不須要寫兩套服務)

9.3    模擬表單文件上傳
1 普通的表單文件上傳
2 multipart方式
3 用HttpClient模擬multipart方式上傳

1 在表單例條件文件
頁面:test.jsp    後臺:af.fileupload.CommonFileUpload
注意:
(1).表單指定enctype="multipart/form-data"
(2).能夠同時普通域和文件域

2.multipart方式的特色
當指定enctype="multipart/form-data"時,瀏覽器將表單裏的每一個字段都生成一個part
抓包分析:...
特色:每一段內容隔開,用於分隔的字符串稱爲分界線(boundary)

3.用HttpClient模擬multipart方式上傳
使用HttpClient,能夠構造符合multipart格式的請求
注意 .addTextBody("id", "123456", TEXT_PLAIN)
第三個參數:指定這個部份內容的編碼方式和content-type  Content-Type:text/plain,charset=UTF-8

public class Test3
{

    public static void doUploadFile(File localfile) throws Exception
    {
        // 1 建立客戶端
        CloseableHttpClient httpclient = HttpClients.createDefault();
            
        // 2 建立請求
        HttpPost httppost = new HttpPost("http://127.0.0.1:8080/demo/CommonFileUpload");    
        
        FileBody filepart = new FileBody(localfile);
        ContentType TEXT_PLAIN = ContentType.create("text/plain", Charset.forName("UTF-8"));
        
        HttpEntity reqEntity =   MultipartEntityBuilder.create()           
                .addTextBody("id", "123456", TEXT_PLAIN)
                .addTextBody("name", "邵發", TEXT_PLAIN)
                .addPart("file", filepart)                
                .build();
        httppost.setEntity(reqEntity);
        
        // 3 執行HTTP請求
         CloseableHttpResponse response = httpclient.execute(httppost);// 發起HTTP
         try
         {
             // 4 解析和處理服務器返回的數據
             
             // 檢查狀態碼
             StatusLine statusLine = response.getStatusLine();
             int status = statusLine.getStatusCode();
             if(status != 200)
             {
                 String reason = statusLine.getReasonPhrase();
                 throw new Exception("Status Error: " + status + "," + reason);
             }
             
             // 保存文件
             HttpEntity entity = response.getEntity();
             String reply = EntityUtils.toString(entity);
             System.out.println("返回結果: \n" + reply);
             
         } finally
         {
             try{response.close();}catch(Exception e){}            
         }        
        
    }
    public static void main(String[] args) throws Exception
    {
        doUploadFile(new File("e:/abc.txt"));
    }
}

9.4    Session的維持
背景:假設後臺提供若干服務,例如,提供機密文件下載服務.可是要求用戶先登陸才能下載.
UserLogin.api    Download.api
經過HttpClient實現

public class Test4_1
{
    
    public static String doPost(String api, String reqText) throws Exception
    {
        // 1 建立客戶端
        CloseableHttpClient httpclient = HttpClients.createDefault();
    
        // 2 建立一個HTTP請求
        // 建立表單請求        
        HttpPost httppost = new HttpPost(api);
        
        StringEntity dataSent = new StringEntity(reqText, ContentType.create("text/plain", "UTF-8"));
        httppost.setEntity(dataSent);    
        
        // 3 執行HTTP請求
        CloseableHttpResponse response = httpclient.execute(httppost);// 發起HTTP
        try
        {
            // 4 解析和處理服務器返回的數據
            
            // 檢查狀態碼
            StatusLine statusLine = response.getStatusLine();
            int status = statusLine.getStatusCode();
            if(status != 200)
            {
                String reason = statusLine.getReasonPhrase();
                throw new Exception("Status Error: " + status + "," + reason);
            }
            
            // 保存文件
            HttpEntity entity = response.getEntity();
            String reply = EntityUtils.toString(entity);
            // System.out.println("返回結果: \n" + reply);
            return reply;
        } finally
        {
            try{response.close();}catch(Exception e){}            
        }            
    }    
    public static void main(String[] args) throws Exception
    {
        String service = "http://127.0.0.1:8080/demo/";
        
        /////////// 用戶登陸 ///////////////
        System.out.println("\n...... 用戶登陸 ......\n");
        JSONObject j1 = new JSONObject();
        j1.put("username", "邵發");
        j1.put("password", "123456");
        String s1 = doPost(service + "UserLogin.api",  j1.toString());
        System.out.println( "結果: " + s1);        
        ////////// 執行更多業務 //////////
        System.out.println("\n...... 下載機密資料 ......\n");
        JSONObject j2 = new JSONObject();
        j2.put("iwant", "something confidential");        
        String s2 = doPost(service + "Download.api",  j2.toString());
        System.out.println( "結果: " + s2);
    }

}
後面兩次請求間沒有關係,Session沒有維持.
全部下載的權限檢查無法經過.

HttpClient支持Cookie,因此能夠很容易的實現Session
實例:MyHttpClient
public class MyHttpClient
{
    CloseableHttpClient httpclient ;
    
    public MyHttpClient()
    {
        // Cookie支持
        RequestConfig config = RequestConfig.custom()
                .setCookieSpec(CookieSpecs.DEFAULT)
                .build();
        httpclient = HttpClients.custom()
                .setDefaultRequestConfig(config)
                .build();
    }


第2種方法:在uri裏傳遞jsessionid
Test4_2.java
普通調用:
http://127.0.0.1:8080/demo/Example.api?id=23
---- session 維持 ----
http://127.0.0.1:8080/demo/Example.api;jsessionid=xxxxxxxxxxxxxxxx?id=23

在uri裏,以分號開頭,加上jsessionid=xxxxxx也能維持Session

小結:介紹了在使用HttpClient的時候,如何維持Session信息
1.讓HttpClient支持Cookie
2.或者直接在uri裏添加;jsession=xxxxx

10.1    SpringMVC
SpringMVC是Spring家族的一個特性,用於WEB開發.官網spring.io
參考<SpringMVC教程>進行配置.
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- spring3  中文表單參數支持  -->
    <filter>  
        <filter-name>spring3encoding</filter-name>  
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
        <init-param>  
            <param-name>encoding</param-name>  
            <param-value>UTF-8</param-value>  
        </init-param>  
        <init-param>  
            <param-name>forceEncoding</param-name>  
            <param-value>true</param-value>  
        </init-param>  
    </filter>  
    <filter-mapping>  
        <filter-name>spring3encoding</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>  
  
  <!--  spring3  support -->    
  <servlet>
    <servlet-name>spring3</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup> 1 </load-on-startup>    
  </servlet>  
  <servlet-mapping>
    <servlet-name>spring3</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>


添加spring3-servlet.xml,  複製web.xml更名爲spring3-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation=" 
           http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
           http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/mvc 
           http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"
    default-autowire="byName"> 
   
   <!-- 描述包含controller的包路徑, 以逗號分隔 -->
   <context:component-scan base-package="my" > </context:component-scan>    
       
   <!-- 默認的註解映射的支持 -->    
   <mvc:annotation-driven />    
       
   <!-- 視圖解釋類 -->    
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    
    <property name="prefix" value="/" />    
    <property name="suffix" value=".jsp"/><!--可爲空,方便實現自已的依據擴展名來選擇視圖解釋類的邏輯  -->    
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />    
   </bean>    
           
    <!-- 對靜態資源文件的訪問  方案一 (二選一) -->    
    <mvc:default-servlet-handler/>    

</beans> 


新建一個packge my,下面新建一個class
HelloController
@Controller
public class HelloController
{
    @RequestMapping("/test1")
    public ModelAndView test1()
    {
        return new ModelAndView ("test1view", "result", "haha");
    }
}

注:包路徑my要配置到spring3-servlet.xml裏,否則spring3不會加載這個類

解釋:Controller控制器
@Controller : 聲明這是一個spring3的controller
@RequestMapping("/test1"): 用於聲明一個url映射: 當訪問 /test1時,請求由此函數處理
new ModelAndView (「test1view」, 「result」, 「haha」)
指定view是 test1view.jsp 進行顯示,同時添加一個屬性result用於存放結果數據。(參考下一章的說明)

添加一個test1view.jsp文件在webroot下面.test1view的名字是由於Controller裏面起名.

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  
  <body>
    spring3mvc:  result=${result} <br>
    
  </body>
</html>

MVC=Controller Model View(控制器,數據模型,視圖)
先由Controller響應,接收數據發給JSP視圖實現.


10.2    SpringMVC(2)自定義的Model
本節課演示一個更復雜的Model
test1view.jsp
  <body>
    <label>學號:</label> ${result.id }<br>
    <label>姓名:</label> ${result.name }<br>
    <label>電話:</label> ${result.cellphone}<br>
  </body>
創建一個簡單的POJO類Student包含int id,String name,String cellphone
控制器    
@Controller
public class GoodController
{
    @RequestMapping("/test1")
    public ModelAndView test1()
    {
        Student result =new Student();
        result.id=20170001;
        result.name="xxx";
        result.cellphone="13999999";
        return new ModelAndView ("test1view", "result", result);
    }
}
Model能夠是一個複雜的對象,只要有Getter就能在View裏訪問.
C:test1.do->test1()
M:result=new Student()
V:test1View.jsp

10.3    SpringMVC(3)獲取請求參數
添加HelloController.java裏面

    @RequestMapping("/test3a")
    public ModelAndView test3a(int id, String name, String cellphone)
    {
        System.out.println("請求參數: " + id + "," + name + "," + cellphone);
        
        return new ModelAndView("test3view", "result", "");
    }    
    
    @RequestMapping("/test3b")
    public ModelAndView test3b(HttpServletRequest request, HttpServletResponse response)
    {
        String id = request.getParameter("id");
        String name = request.getParameter("name");
        String cellphone = request.getParameter("cellphone");
        System.out.println("請求參數3b: " + id + "," + name + "," + cellphone);
        
        return new ModelAndView("test3view", "result", "");
    }
    
    @RequestMapping("/test3c")
    public ModelAndView test3c(Student stu)
    {
        System.out.println("請求參數3c: " + stu.id + "," + stu.name + "," + stu.cellphone);
        
        return new ModelAndView("test3view", "result", "");
    }

添加對應頁面test3.jsp
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head> 
  <body>
   <form class='myform' method='post' action='test3a.do' >
       
            <input type='text' name='id'  placeholder='學號' /> <br>
            <input type='text' name='name' placeholder='姓名' /> <br>
            <input type='text' name='cellphone' placeholder='手機號' /> <br>    
            <input type='submit' value='提交' />
   </form>
  </body>
</html>
還有跳轉頁面test3view.jsp

SpringMVC會自動根據參數的名稱和類型,將參數值傳入處處理函數。
三種方式:
form字段 <->  函數參數
request中獲取 : 
映射爲pojo  : 對應struts的ModelDriven,經過映射的方式把Student POJO類直接取到

10.4    SpringMVC(4)RESTful接口的實現
RESTful    客戶端>>json>>服務器
    客戶端<<json<<服務器
@responseBody註解的做用是將controller的方法返回的對象經過適當的轉換器轉換爲指定的格式以後,
寫入到response對象的body區,一般用來返回JSON數據或者是XML

@Controller
public class HelloController
{
    //////////////// 第4節課 ////////////////
    @ResponseBody
    @RequestMapping(value ="/test4", produces = "text/plain; charset=utf-8")     //設置Controller的響應URL,設置格式
    public String test4 ( @RequestBody String str ) throws Exception        //設置字符編碼utf-8
    {        
        JSONObject req = new JSONObject(URLDecoder.decode(str, "UTF-8"));
        JSONObject resp = new JSONObject();
        resp.put("a", req.getString("name"));
        resp.put("b", 1);
        return resp.toString( 4 );
    }
}
返回值爲一個字符串,而非ModelAndView.

///////////前端頁面
添加前端頁面test4.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">    
    <!-- jquery 和 bootstrap 支持 -->
    <script src="jquery/jquery.js"></script>
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
    <script src="bootstrap/js/bootstrap.min.js"></script>    
    <link href="bootstrap/css/toastr.min.css" rel="stylesheet" /> 
    <script src="bootstrap/js/toastr.min.js"></script> 
    <script src="js/AfUtility.js"></script>  
  </head>
  <body> 
       <script>
           function test_restapi()
           {
               var req = {};
               req.id = 123;
               req.name = "邵發";
               
               Af.rest ("test4.do", req, function(ans)
               {
                   Af.trace(ans);
               });
           }       
           test_restapi();
       </script>   
  </body>
</html>
//////////////

1  @ResponseBody
2  返回值是String
3  produces = "text/plain; charset=utf-8"
4  @RequestBody String str
相關文章
相關標籤/搜索