网站高级

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

 

posted @ 2019-03-18 14:16  ricky0001  阅读(274)  评论(0编辑  收藏  举报