目录:
第五章 异步请求和异步响应
5.1 Web前端技术
HTML元素属性分为核心属性、事件属性和外观属性。例如下面<input/>标签定义了一个单行文本框。
<input style="text-align:center;color:red;font-size:20px;border:10px solid blue;"onbulr="alert(this.value)" type="text" name="useName" id="useName" value=""/>
核心属性是,id、type、name、value
外观属性是CSS代码,定义了HTML元素的外观。style属性定义了HTML元素的行内CSS样式,HTML元素的class属性为当前HTML元素应用一个或者多个CSS样式类,多个CSS样式用空格隔开即可。
事件属性:JavaScript来定义。例如onblur属性就是一个事件属性,οnblur="alert(this.value)"的功能是:向单行文本框中输入文字(例如abcd)当单行文本框失去焦距的时候,触发
alert(this.value),该效果是弹出一个对话框,对话框上显示单行文本框输入的内容。this等效本HTML元素,this.value指代本HTML元素的value属性。
以下是常用的事件属性举例如下:
<body>标签常用的事件属性是 onload ,表示HTML页面加载到浏览器后,触发执行 onload 属性定义的行为
单行文本框<input>标签(type=‘text’)常用事件属性是 onblur ,当单行文本框元素失去焦点触发执行 onblur 事件属性定义的行为。
事件属性 onchange ,当内容改变且失去焦点,触发执行onchange属性定义的行为。
事件属性 onclick ,当HTML元素上发生单击事件,触发执行onclick属性定义的行为
事件属性 onbclick ,双击触发
了解CSS
事前工作:
Dynamic Web Project 项目ajax
servlet-api.jar包导入Web项目,WebContent目录下创建CSS.html静态页面程序。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>层叠特性</title>
<style type="text/css">
p{ /*这是一个标签选择器,本样式适用所有<p>标签*/
color:green;
}
.redColor{ /*类选择器,用.定义,本样式适用所有class=‘redColor’的标签*/
color:red;
}
.blackColor{
color:black;
}
#line3{ /*ID选择器,用#定义,本样式适用所有class=‘redColor’的标签*/
color:blue;
}
</style>
</head>
<body>
<p>第一行文本是绿色</p>
<p class="redColor">第二行红色文本</p>
<p id="line3" class="redColor">第三行蓝色文本</p>
<p style="color:orange;" id="line3">第四行橙色文本</p>
<p class="blackColor redColor">第五行黑色文本</p>
<p class="redColor blackColor">第六行黑色文本</p>
</body>
</html>
类选择器优先级大于标签选择器,类选择器优先级是按照CSS样式表的顺序决定的,后面样式覆盖前面的样式。
小露身手:演示HTML元素和JavaScript呈现的顺序
在WebContent目录下创建js.html静态页面程序,将代码修改为如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>层叠特性</title>
</head>
<script>
document.write("<h1>title标签中的JavaScript代码</h1>");
document.write("<h2>" + document.url + "</h2>");
document.write("<h2>" + document.URL + "</h2>");
document.write("<h2>" + document.characterSet + "</h2>");
document.write("<h2>" + document.contentType + "</h2>");
</script>
<body>
<h1>body代码</h1>
<input style="text-align:center;color:red;font-size:20px;border:10px solid:blue;"
onblur="alter(this.value)" type='text' name='userName' id="userName" value="" />
<script>
document.write("<h1>body结束标签前的JavaScript代码</h1>");
</script>
</body>
</html>
值得注意的是只有document.URL
通过JavaScript的document对象操作HTML元素
定位元素:
document.getElementById("userName");
document.getElementByName("interest");//此时返回的是所有名字叫interest的html元素的数组,例如单选按钮等
document.getElementByTagName(tagName);//较少用
读取或设置HTML元素的内容:
document.getElementById(id).innerHTML = new text or new HTML element;//设置内容
var id = document.getElementById(id).innerHTML;//读取
读取或设置HTML元素属性的值:
document.getElementById(id).attribute = new value;
var attribute = document.getElementById(id).attribute;
设置HTML元素style属性的属性值
document.getElementById(id).style.样式名 = "样式值";//添加或修改style样式
document.getElementById(id).style.样式名 = "";//删除样式
innerHTML和innerText区别:innerHTML可以读取设置HTML元素所包含的HTML标签和文本信息,innerText用于读取或者设置当前HTML元素所包含的文本信息。另外,仅部分浏览器支持innerText
小露身手:通过JavaScript的document对象操作HTML元素内容
准备存储图片文件夹。WebContent目录下新建文件夹pictures,再放入图片即可。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<script>
var showMessage = function (input){
var userName = input.value.trim();//两端空格去除
var userNameMessage = document.getElementById("userNameMessage");
if(userName==""){
userNameMessage.style.color = "red";
userNameMessage.innerHTML="用户不能为空!";
return;
}
if(userName == "zhangsan"){
userNameMessage.style.color = "red";
userNameMessage.innerHTML="用户已被占用!"
}
else{
userNameMessage.style.color = "green";
userNameMessage.innerHTML = "用户可以使用!"
}
}
function showPicture(){
document.getElementById("show").innerHTML=
"<img width=200 src='<%= request.getContextPath()%>/pictures/屑狐狸.jpg'>";
}
</script>
</head>
<body>
<input onblur="showMessage(this)" type='text'/><span id='userNameMessage'></span>
<br/>
<input type="submit" onclick="showPicture()" value="加载图片"><span id="show"></span>
</body>
</html>
onblur是元素失去焦点,执行showMessage()函数
picture目录必须位于Web Content目录下,原因在上一章节提到
<span></span>标签通常组合HTML页面的行内元素
行内元素、块级元素、行内块级元素的比较如下:
行内元素:相邻的行内元素不换行,设置的宽度高度无效,外边距margin和内边距padding仅在水平方向有效,垂直方向无效。
块级元素:能自动换行,能设置宽高,外边距margin和内边距padding对上下左右四个方向的设置均有效。
行内块级元素:元素排列一行,不自动换行,宽度、高度、外边距margin、内边距padding对上下左右4个方向设置均有效。
常见的行内元素有<span><img><a><big><small><strong><u>
块级元素<div><p><h1>~<h6><table><ul><li><ol>等以及HTML5新增的属性<header><section><aside><footer>
三者的转换:
display:inline;
display:block;
display:inline-block
5.3 异步请求和异步响应
概述:
区别于同步请求,异步请求就是允许多个请求同步发出,执行下一个操作之前不需要等待上一个操作返回的执行结果。
AJAX:不重新加载当前页面的前提下,实现HTML的局部刷新。
XMLHttpRequest异步请求对象
XMLHttpRequest异步请求是ajax的核心
创建异步请求对象(主流浏览器): var request = new XMLHttpRequest();
IE浏览器不支持XMLHttpRequest对象,如下创建异步请求对象:
var request = new ActiveXObject("Microsoft.XMLHTTP");
XMLHttpRequest异步请求对象的辅助函数:
假设request是一个XMLHttpRequest异步请求对象的实例化对象
request.setRequestHeader(header:String,value:String):设置异步请求的请求头
request.getAllResponseHeaders():返回异步响应的响应头列表,返回字符串类型的数据
request.getResponseHeader(header:String):返回异步响应中指定响应头的响应头值
异步响应依赖于异步请求
servlet既要接受HTTP请求,也返回HTTP响应,JavaScript是浏览器端技术,异步JavaScript则是使用request异步请求对象模拟浏览器发出HTTP请求并且接受HTTP响应
XMLHttpRequest异步请求对象的核心函数:
request.open():
request.open(method:String,URL:String;async=true:Boolean):该函数没有返回值
method设置请求的数据发送方式(通常是get或者post);URL设置请求访问的URL。参数async默认值为true,表示请求为异步请求。通常情况最好使用异步请求,以便浏览器在请求时保持响应
request.send():无返回值
只有调用send函数,请求数据才会发出
request.send():发送没有请求体的GET请求或者POST请求
request.send(data:FormData):发送data数据,通常发送POST请求,自动将FormData数据data封装到POST请求体中,默认情况下,POST请求体中的数据采用multipart/form-data编码
request.send(data:String):发送data字符串类型数据,通常用于发送POST请求,data封装到POST里面。如data有中文,则要改编码.
JavaScript示例代码如下:
var userName = ecodeURI("张三丰");
var data = "action=checkUserName&userName=" + userName;
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
请求书封装到POST请求体前,需要指定POST请求体的MIME
FormData对象
request.send(data:FormData)函数一起使用,将异步请求的数据“伪装成”一个multipart/form-data内容格式的表单数据,创建FormData对象的代码如下:
var form = new FormData();
FormData()核心函数是append(),该函数以“name/value”的方式将异步请求数据附加到FormData对象,无返回值。
form.append(name:String,value:String)
XMLHttpRequest 异步请求对象的核心属性
(1)request.readyState:表示当前异步请求的状态(5种)
0:UNSENT :创建,但未指定请求的URL和数据发送方式
1:OPENED:指定了请求的URL和数据发送方式,但request.send()函数未调用
2:HEADERS_RECEIVED:Web服务器接收到异步请求并返回响应状态码和响应头列表,浏览器接收到了响应状态码和响应头列表
3:LOADING:浏览器加载响应体数据
4:DONE:加载完所有响应体数据,响应体中数据处于就绪状态
request.responseType:设置响应数据的数据类型,响应数据的数据类型有5种:text、arrayBuffer、blob、document、json,默认是text类型。
request.responseType = 'text';
必须在异步响应状态loading之前,设置响应数据的数据类型。
request.responseText:获取响应的文本型数据
request.status:获取响应状态码 //技巧:异步请求的状态码是4,并且响应状态码等于200时,再获取响应的文本型数据。
XMLHttpRequest异步请求对象的事件监听
onreadystatechange事件用于监听 readyState属性值的变化,异步请求的属性值变化,就会触发事件
小露身手:了解GET异步请求和异步响应的执行流程
ajax Web 项目展开 Java Resources,src创建Servlet
doGet()方法,修改为如下代码:
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/XHRServlet")
public class XHRServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public XHRServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String action = request.getParameter("action");
if("测试XHR".equals(action)) {
/*String request.getMethod():返回浏览器发出请求时使用的数据发送方式,例GET、POST
String request.getScheme():返回浏览器发出请求时使用的协议,如HTTP、HTTPS
String request.getProtocol():返回浏览器发出请求时使用的协议版本,例如HTTP/1.1
String request.getQueryString():返回浏览器发出请求时的查询字符串(被URL编码),若无返回null
* */
System.out.println("获取" + request.getMethod() + "请求行信息");//网址类的东西
System.out.println(request.getScheme());
System.out.println(request.getMethod());
System.out.println(request.getProtocol());
System.out.println(request.getQueryString());
System.out.println("获取" + request.getMethod() + "请求头信息");//内部配置
String userAgentValue = request.getHeader("user-agent");
System.out.println("User-Agent请求头值是:" + userAgentValue);
java.util.Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
java.util.Enumeration<String> headerValues = request.getHeaders(headerName);
System.out.println("请求头:"+ headerName + ",对应的请求头值有:");
while(headerValues.hasMoreElements()) {
String headerValue = headerValues.nextElement();
System.out.println(headerValue);
}
}
System.out.println("获取所有参数名以及对应的所有参数值");
java.util.Enumeration<String> paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
System.out.println("参数" + paramName + "的值是:");
for(String paramValue : paramValues) {
System.out.println(paramValue);
}
}
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("XHRServlet的响应");
}
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
WebContent目录创建xhr0.jsp程序,将代码修改如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript">
var testXHR = function(){
var request = new XMLHttpRequest();
request.onreadystatechange = function(){
if(request.readyState==1){
var result = request.responseText;
alert("状态请求码1时,响应状态码是" + request.status + "响应数据" + result);
alert("响应头是:" + request.getAllResponseHeaders());
}
if(request.readyState==2){
var result = request.responseText;
alert("状态请求码2时,响应状态码是" + request.status + "响应数据" + result);
alert("响应头是:" + request.getAllResponseHeaders());
}
if(request.readyState==3){
var result = request.responseText;
alert("状态请求码3时,响应状态码是" + request.status + "响应数据" + result);
alert("响应头是:" + request.getAllResponseHeaders());
}
if(request.readyState==4){
var result = request.responseText;
alert("状态请求码4时,响应状态码是" + request.status + "响应数据" + result);
alert("响应头是:" + request.getAllResponseHeaders());
var xhr = document.getElementById("xhr");
xhr.innerHTML = result;
}
};
alert("open()执行前的请求状态码" + request.readyState + "响应状态码" + request.status);
request.open("get","<%=request.getContextPath()%>/XHRServlet?action=测试xhr");
alert("open()执行后send()执行前请求状态码" + request.readyState + "响应状态码" + request.status);
request.send();
alert("send()执行后的请求状态那" + request.readyState + "响应状态码" + request.status);
}
</script>
</head>
<body>
<input type="submit" onclick="testXHR()" value="测试XHR"><span id="xhr"></span>
</body>
</html>
小露身手:使用XMLHttpRequest 发送异步的POST请求
WebContent下创建xhr1.jsp程序
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
var showMessage = function(input){
var userName = encodeURI(input.value.trim());//将单行文本的数据编码成了对应的UTF-8十六进制编码数编码
if(userName==""){
userNameMessage.style.color = "red";
userNameMessage.innerHTML="用户名不能为空";
return;
}
var request = new XMLHttpRequest();
request.open("POST","<%=request.getContextPath()%>/XHRServlet");
var data = "action=checkUserName&userName=" + userName;
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
request.send(data);
request.onreadystatechange = function(){
if(request.readyState == 4 && request.status == 200){
var userNameMessage = document.getElementById("userNameMessage");
var text = request.responseText;
userNameMessage.innerHTML = text;
}
}
}
</script>
</head>
<body>
<input onblur="showMessage(this)" type='text'/><span id='userNameMessage'></span>
</body>
</html>
XHRServlet方法doPost改写如下:
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/XHRServlet")
public class XHRServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public XHRServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String action = request.getParameter("action");
if("测试XHR".equals(action)) {
/*String request.getMethod():返回浏览器发出请求时使用的数据发送方式,例GET、POST
String request.getScheme():返回浏览器发出请求时使用的协议,如HTTP、HTTPS
String request.getProtocol():返回浏览器发出请求时使用的协议版本,例如HTTP/1.1
String request.getQueryString():返回浏览器发出请求时的查询字符串(被URL编码),若无返回null
* */
System.out.println("获取" + request.getMethod() + "请求行信息");//网址类的东西
System.out.println(request.getScheme());
System.out.println(request.getMethod());
System.out.println(request.getProtocol());
System.out.println(request.getQueryString());
System.out.println("获取" + request.getMethod() + "请求头信息");//内部配置
String userAgentValue = request.getHeader("user-agent");
System.out.println("User-Agent请求头值是:" + userAgentValue);
java.util.Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
java.util.Enumeration<String> headerValues = request.getHeaders(headerName);
System.out.println("请求头:"+ headerName + ",对应的请求头值有:");
while(headerValues.hasMoreElements()) {
String headerValue = headerValues.nextElement();
System.out.println(headerValue);
}
}
System.out.println("获取所有参数名以及对应的所有参数值");
java.util.Enumeration<String> paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
System.out.println("参数" + paramName + "的值是:");
for(String paramValue : paramValues) {
System.out.println(paramValue);
}
}
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("XHRServlet的响应");
}
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
request.setCharacterEncoding("UTF-8");
String action = request.getParameter("action");
if("checkUserName".equals(action)) {
response.setContentType("text/html;charset=UTF-8");
String userName = request.getParameter("userName");
if(userName==null || "".equals(userName.trim())) {
response.getWriter().append("<font color='red'>用户名不能为空</font>");
return;
}
userName = new String((userName.getBytes("ISO-8859-1")),"UTF-8");//必须是8859
//先将接收到的数据用ISO解码成字节码,再用UTF-8重新编码
System.out.println(userName);
java.util.ArrayList<String> userNames = new java.util.ArrayList<String>();
userNames.add("zhangsan");
userNames.add("lisi");
userNames.add("wangwu");
if(userNames.contains(userName)) {
response.getWriter().append("<font color='red'>该用户名已经被占用</font>");
}
else {
response.getWriter().append("<font color='green'>该用户名可以使用</font>");
}
}
}
}
小露身手:使用XMLHttpRequest和FormData异步上传、显示图片
Servlet程序支持文件上传操作,必须确保向Servlet程序添加如下注解
@javax.servlet.annotation.MultipartConfig
WebContent目录创建xhr2.jsp程序,修改为如下所示:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
var uploadPicture = function(file){
var form = new FormData();
form.append("picture",file.file[0]);//上传的文件也被传入FromData
form.append("action","uploadPicture");
var request = new XMLHttpRequest(){
request.open("POST","<%= request.getContextPath()%>/XHRServlet");
request.send(form);
request.onreadystatechange = function(){
if(request.readyState==4 && request.status == 200){
var text = request.responseText;
var picture = document.getElementById("picture");
picture.innerHTML = text;
}
}
}
}
</script>
<body>
<input type="file" onchange="uploadPicture(this)"/><span id="picture"></span>
</body>
</html>
onchange属性是事件属性
本任务仅演示单文件上传的功能,这里的file.file[0]获取的是文件上传框的第一个文件
向XHRServlet.java的doPost()方法新增如下代码:
if("uploadPicture".equals(action)) {
//上传文件必须使用POST请求,使用FormData发送异步POST请求体中数据默认multipart/form-data编码
//XHRServlet 的doPost方法,action是uploadPicture
/*上传后的文件名默认为userName+扩展名,一是限制头像数量二是防止中文文件名乱码,这里的userName固定为001
* 文件存储在picture目录下,必须要先创建文件夹
* 判断文件是否为图片
*
* */
request.setCharacterEncoding("UTF-8");
String pictureDIR = "/pictures/";
String userName = "001";
String picturePath = request.getServletContext().getRealPath(pictureDIR);
javax.servlet.http.Part singlePart = request.getPart("picture");//???
response.setContentType("text/html;charset=UTF-8");
if(singlePart != null) {
String singleFileContentType = singlePart.getContentType();
//显示上传文件使用的技术是利用<img/>标签的src属性,向Servlet发送异步请求GET
if(singleFileContentType.contains("image/")) {
String singleFileName = singlePart.getSubmittedFileName();
if(singleFileName != null && !"".equals(singleFileName)) {
String suffix = singleFileName.substring(singleFileName.lastIndexOf("."));
singleFileName = userName + suffix;
System.out.println("上传的文件是:"+picturePath+singleFileName);
singlePart.write(picturePath + singleFileName);
singlePart.delete();
String contextPath = request.getContextPath();
/*避免浏览器访问浏览器缓存的图片,src中加入了时间戳
* */
long time = System.currentTimeMillis();
String src = contextPath + "/XHRServlet?action=showPicture&fileName=" + singleFileName + "&" + time;
response.getWriter().append("<img height=150 src=" + src + " />");///XHRServlet。。。的/不能省略
return;
}
}
}
向XHRServlet.java 新增doGet()的方法
if("showPicture".equals(action)) {
String fileName = request.getParameter("fileName");
String path=this.getServletContext().getRealPath("/pictures/"+fileName);
java.io.File file = new java.io.File(path);
java.io.FileInputStream fis = new java.io.FileInputStream(file);
String mime = request.getServletContext().getMimeType(fileName);
response.setContentType(mime);
response.setContentLength((int)file.length());
javax.servlet.ServletOutputStream sos = response.getOutputStream();
byte[] bytes = new byte[1024*4];
int len = 0;
while((len=fis.read(bytes))!=-1) {
sos.write(bytes,0,len);
}
fis.close();
sos.close();
}
结果不展示