1. 概述

传统请求及缺点

传统的请求有哪些?

  • 直接在浏览器地址栏输入 URL
  • 点击超链接
  • 提交 form 表单
  • 使用 JS 代码发送请求
    • window.open(url)
    • document.location.href = url
    • window.location.href = url

存在的问题:

  • 页面会全部刷新,导致用户体验差
  • 传统请求会使用户体验不连贯

关于 AJAX

Asynchronous JavaScript And XML(异步的 JavaScript 和 XML)
AJAX 代码属于 JS 代码
AJAX 不是新的编程语言,而是一种使用现有标准的新方法,解决了传统请求存在的问题
AJAX 最大的优点:可以在不重新加载页面的情况下,与服务器交换数据并更新部分内容(局部刷新)

异步和同步

  • 异步
    客户端不需要等待服务器端的响应。在服务器处理请求的过程中,客户端可以进行其他的操作
    ajax 请求1和 ajax 请求2,同时并发,谁也不用等谁
  • 同步
    客户端必须等待服务器端的响应。在等待的期间客户端不能做其他操作
    ajax 请求1在发送时,需要等待 ajax 请求2结束之后才能发送

2. 核心:XMLHttpRequest 对象

XMLHttpRequest 对象是 AJAX 的核心对象,负责发送请求和接收服务器返回的数据

目前的浏览器都内置了该对象

使用
创建 XMLHttpRequest 对象:

1
var xhr = new XMLHttpRequest();

相关方法

方法 描述
abort() 取消当前请求
getAllResponseHeaders() 返回头部信息
getResponseHeader() 返回特定的头部信息
open(method, url, async, user, psw) 规定请求。
method: 请求方式
url: 文件位置
async: 异步或同步
user: 可选的用户名称
psw: 可选的密码
send() 将请求发送到服务器,用于 GET 请求
send(string) 将请求发送到服务器,用于 POST 请求
setRequestHeader() 向要发送的报头添加标签/值对

相关属性

属性 描述
onreadystatechange 定义当 readyState 属性发生变化时被调用的函数
readyState 保存 XMLHttpReqeust 对象的状态。
0:请求未初始化
1:服务器连接已建立
2:请求已收到
3:正在处理请求
4:请求已完成且响应已就绪
responseText 以字符串返回响应数据
responseXML 以 XML 数据返回响应数据
status 返回请求的状态号。
200:”OK”
403:”Forbidden”
404:”Not Found”
statusText 返回状态文本(如:”OK” 或 “Not Found”)

3.1 GET 请求

示例代码

ajax.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AJAX get请求</title>
<script type="text/javascript">
window.onload = function () {
document.getElementById("myBtn").onclick = function () {
// 第一步:创建 AJAX 核心对象
const xhr = new XMLHttpRequest();
// 第二步:注册回调函数
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200) {
document.getElementById("myDiv").innerHTML = this.responseText;
} else {
alert(this.status)
}
}
}
// 第三步:开启通道
xhr.open("GET", "/ajax/ajaxrequest01", true);
// 第四步:发送请求
xhr.send();
}
}
</script>
</head>
<body>
<!-- 用户点击按钮时,发送 ajax 请求 -->
<button id="myBtn">发送 ajax 请求</button>
<!-- ajax 接收响应的数据后,对 div 进行渲染 -->
<div id="myDiv"></div>
</body>
</html>

AjaxRequestServlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author ShameYang
* @date 2023/9/15 14:16
* @description
*/
@WebServlet("/ajaxrequest01")
public class AjaxRequestServlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("<font color='red'>请求发送成功!</font>");
}
}

缓存问题

  • AJAX 请求缓存问题
    • HTTP 协议中规定:get 请求会被缓存起来
    • 发送 AJAX GET 请求时,对于低版本的浏览器来说,第二次的请求会走缓存,不走服务器
  • POST 请求不会被浏览器缓存起来
  • GET 请求缓存的优缺点
    • 优点:直接从浏览器缓存中获取资源,不需要从服务器上重新加载,速度快,用户体验好
    • 缺点:无法实时获取最新的服务器资源
  • 浏览器什么时候走缓存?
    • GET 请求
    • 请求路径被浏览器缓存了,第二次发送请求时,路径没有变化
  • 低版本的浏览器怎么解决 AJAX GET 请求的缓存问题?
    • 在请求路径 url 后边添加时间戳或随机数,这样每次的路径都会发生改变
    • “url?t=” + new Date().now()
    • “url?t=” + Math.random()

3.2 POST 请求

与 GET 请求的区别:

  • 只有前端代码有区别
  • post 请求提交的数据在 send 方法中,而 get 请求提交的数据在 open 中
  • post 请求需要设置请求头的内容类型,get 请求不需要

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax post</title>
<script type="text/javascript">
window.onload = function () {
document.getElementById("myBtn").onclick = function () {
// 第一步:创建 AJAX 核心对象
const xhr = new XMLHttpRequest();
// 第二步:注册回调函数
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200) {
document.getElementById("mySpan").innerHTML = this.responseText;
} else {
alert(this.status);
}
}
}
// 第三步:开启通道
xhr.open("POST", "/ajax/ajaxrequest02", true);
// 设置请求头的内容类型
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// 第四步:发送请求
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
xhr.send("username=" + username +"&password="+ password);
}
}
</script>
</head>
<body>
用户名:<input type="text" id="username" name="username"><br>
密码:<input type="password" id="password" name="password"><br>
<button id="myBtn">发送 AJAX 请求</button><br>
<span id="mySpan"></span>
</body>
</html>

案例

使用 AJAX POST 请求实现验证用户名
步骤:

  • 前端:用户输入用户名后,失去焦点事件 blur 发生,然后发送 AJAX POST 请求,提交用户名
  • 后端:接收用户名后,连接数据库,查询是否可用

ajax.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>verify username</title>
<script type="text/javascript">
window.onload = function () {
document.getElementById("username").onblur = function () {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200) {
document.getElementById("mySpan").innerHTML = this.responseText;
} else {
alert(this.status);
}
}
}
xhr.open("POST", "/ajax03/ajaxrequest03", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
const username = document.getElementById("username").value;
xhr.send("username=" + username);
}
document.getElementById("username").onfocus = function () {
document.getElementById("mySpan").innerHTML = "";
}
}
</script>
</head>
<body>
用户名:<input type="text" id="username" name="username"><span id="mySpan"></span><br>
密码:<input type="password" id="pwd" name="pwd"><br>
</body>
</html>

AjaxRequestServlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* @author ShameYang
* @date 2023/9/15 16:19
* @description
*/
@WebServlet("/ajaxrequest03")
public class AjaxRequestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
boolean flag = true;
try {
conn = DBUtil.getConnection();
String sql = "select username from t_user where username=?";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
rs = ps.executeQuery();
if (rs.next()) {
flag = false;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(conn, ps, rs);
}

response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
if (flag) {
out.print("<font color='green'>用户名可用</font>");
} else {
out.print("<font color='red'>用户名已存在!</font>");
}
}
}

4. 数据交换

基于 JSON 的数据交换

在 WEB 前端中,将一个 json 格式的字符串转换为 json 对象的方法:

  • eval 函数
    1
    2
    var fromJava = "{\"name\": \"tom\", \"gender\": \"man\"}";
    window.eval("var jsonObj = " + fromJava);
  • 内置对象 JSON 的 parse 方法
    1
    2
    var jsonStr = "{\"username\" : \"xxx\"}";
    var jsonObj = JSON.parse(jsonStr);

fastjson

使用阿里巴巴的 fastjson 组件可以非常便利地将 java 对象转换成 json 格式的字符串
注意:需要导入 fastjson 的 jar 包

1
String jsonStr = JSON.toJSONString();

基于 XML 的数据交换

如果服务器端响应 XML 的话,需要修改响应的内容类型

1
response.setContentType("text/xml;charset=UTF-8");

补充:AJAX 乱码问题

Tomcat 9以及之前版本,需要设置字符集,否则会出现中文乱码问题

解决方案:

  • 响应时乱码
    1
    response.setContentType("text/html;charset=UTF-8");
  • 发送 AJAX post 请求时,服务器接收乱码
    1
    request.setCharacterEncoding("UTF-8");

5. AJAX 代码封装到 jQuery 库

jQuery 是一个轻量级的 JavaScript 库,可以简化 JS 代码的编写。这里我们手动封装一个 jQuery 库

手动开发 jQuery 源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
function jQuery(selector) {
if (typeof selector == "string"){
if (selector.charAt(0) === "#") {
// 全局变量
domObj = document.getElementById(selector.substring(1));
return new jQuery();
}
}
// window.onload
if (typeof selector == "function") {
window.onload = selector;
}
// 定义 html() 函数,代替 domObj.innerHTML = ""
this.html = function(htmlStr) {
domObj.innerHTML = htmlStr;
}
// 定义 click() 函数,代替 domObj.onclick = function(){}
this.click = function(fun) {
domObj.onclick = fun;
}
// 定义 val() 函数,代替 domObj.value
this.val = function(v) {
if (v == undefined) {
return domObj.value;
} else {
domObj.value = v;
}
}

/**
静态方法,发送 ajax 请求
type 请求方式
url 请求的 URL
data 请求时提交的数据
async 请求是否为异步请求
*/
jQuery.ajax = function(jsonArgs) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.states === 200) {
// 假设服务器返回的都是 json 格式的字符串
var jsonObj = JSON.parse(this.responseText);
// 调用函数
jsonArgs.success(jsonObj);
}
}
}

if (jsonArgs.type.toUpperCase() === "POST") {
xhr.open("POST", jsonArgs.url, jsonArgs.async);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(jsonArgs.data);
}

if (jsonArgs.type.toUpperCase() === "GET") {
xhr.open("GET", jsonArgs.url + "?" + jsonArgs.data, jsonArgs.async);
xhr.send();
}
}
}
$ = jQuery

// 让静态方法 ajax 生效
new jQuery()

使用以上 jQuery 库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script type="text/javascript" src="jQuery 的路径"></script>

<script type="text/javascript">
// window.onload
$(function(){
$("#btn1").click(function(){
$.ajax({
type : "",
url : "",
data : "username=" + $("#username").val(),
async : true | false,
success : function(json){
$("#div1").html(json.username);
}
})
})
})
</script>

6. 跨域问题

跨域

  • 跨域指从一个域名的网页请求另一个域名的资源
  • 传统的请求,能直接修改地址的,都能实现跨域访问。但是 AJAX 请求由于 同源策略 的存在,无法跨域访问

同源策略

  • 同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性
  • 为了保护网站信息的安全策略
  • 同源三要素
    • 协议
    • 域名
    • 端口号

实现 AJAX 跨域

方案1:设置响应头

1
response.setHeader("Access-Control-Allow-Origin", "允许跨域的 URL"); // 如果设置为 * 号,则允许所有的 URL 跨域访问

方案2:jsonp

注意:jsonp 解决跨域问题时,只支持 GET 请求

jsonp:json with padding(带填充的 json)

jsonp 不是 ajax 请求,但是可以完成局部刷新的效果,并且可以解决跨域问题

本质上,是用 script 标签进行跨域访问

  • 打开页面时,跨域访问

    1
    <script type="text/javascript" src="URL"></script>
  • 点击按钮,跨域访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <script type="text/javascript">
    // ES 6 的新写法
    window.onload = () => {
    document.getElementById("btn").onclick = () => {
    // 创建 script 元素对象
    const htmlScriptElement = document.createElement("script");
    // 设置 type
    htmlScriptElement.type = "text/javascript";
    // 设置 src
    htmlScriptElement.src = "URL";
    // 添加到 body 标签中
    document.getElementsByTagName("body")[0].appendChild(htmlScriptElement);
    }
    }
    </script>

    <button id="btn">我是按钮,点击实现跨域访问</button>

方案3:jQuery 封装的 jsonp

引入 jQuery 库的 js 文件,直接使用封装好的 jsonp

这个 jsonp 就是方案2的高度封装,底层原理完全相同(只是一个工具,会用就行)

示例代码:
一个域中的 ajax.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery 封装的 jsonp</title>
</head>
<body>
<script type="text/javascript" src="/ajax/js/jquery.min.js"></script>

<script type="text/javascript">
$(function () {
$("#btn").click(function () {
// 类 ajax 请求,不是真正的 ajax 请求
$.ajax({
type: "GET",
url: "http://localhost:8081/for_jsonp/jsonp",
dataType: "jsonp",
success: function (data) {
$("#myDiv").html("欢迎您," + data.username);
}
})
})
})
</script>

<button id="btn">我是按钮</button>
<div id="myDiv"></div>
</body>
</html>

另一个域的 JsonpServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @author ShameYang
* @date 2023/9/17 15:42
* @description
*/
@WebServlet("/jsonp")
public class JsonpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String callback = request.getParameter("callback");
response.getWriter().print(callback + "({\"username\":\"tom\"})");
}
}

方案4:代理机制(httpclient)

怎么使用 Java 程序发送 get/post 请求?

  • JDK 内置的 API(java.net.URL…)
  • 第三方的开源组件,例如:apache 的 httpclient 组件

方案5:nginx 反向代理

  • nginx 反向代理中也是使用了上边的这种代理机制来完成 AJAX 的跨域,实现起来只需要修改一个 nginx 的配置即可。以后会学的~

7. AJAX 实现功能

省市联动

  • 什么是省市联动?
    在网页上,选择对应的省份之后,动态的关联出该省份对应的市。选择对应的市后,动态的关联出对应的区

  • 数据库表设计

    1
    2
    3
    4
    5
    6
    7
    8
    9
    t_area(区域表)
    id(PK-自增) code name pcode
    -------------------------------------
    1 001 北京市 null
    2 002 上海市 null
    3 003 朝阳区 001
    4 004 海淀区 001
    5 005 闵行区 002
    6 006 徐汇区 002
  • 前端代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>省市联动</title>
    </head>
    <body>
    <!-- 引入自己写的 jQuery 库 -->
    <script type="text/javascript" src="/ajax04/js/jQuery-1.0.0.js"></script>
    <script>
    $(function () {
    $.ajax({
    type: "get",
    url: "/ajax04/listArea",
    data: "",
    async: true,
    success: function (jsonArr) {
    var html = "<option value=''>--请选择省份--</option>";
    for (var i = 0; i < jsonArr.length; i++) {
    var area = jsonArr[i];
    html += "<option value='" + area.code + "'>" + area.name + "</option>";
    }
    $("#province").html(html);
    }
    })

    $("#province").change(function () {
    $.ajax({
    type: "get",
    url: "/ajax04/listArea",
    data: "pcode=" + this.value,
    async: true,
    success: function (jsonArr) {
    var html = "<option value=''>--请选择市--</option>";
    for (var i = 0; i < jsonArr.length; i++) {
    var area = jsonArr[i];
    html += "<option value='" + area.code + "'>" + area.name + "</option>";
    }
    $("#city").html(html);
    }
    })
    })
    })
    </script>

    <select id="province"></select>

    <select id="city"></select>
    </body>
    </html>
  • 后端代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    @WebServlet("/listArea")
    public class ListAreaServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    String pcode = request.getParameter("pcode");
    List<Area> areas = new ArrayList<>();
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
    Class.forName("com.mysql.cj.jdbc.Driver");
    String url = "jdbc:mysql://localhost:3306/forajax";
    String user = "root";
    String password = "123456";
    conn = DriverManager.getConnection(url, user, password);
    String sql = "";
    if (pcode == null) {
    sql = "select code, name from t_area where pcode is null";
    ps = conn.prepareStatement(sql);
    } else {
    sql = "select code, name from t_area where pcode = ?";
    ps = conn.prepareStatement(sql);
    ps.setString(1, pcode);
    }
    rs = ps.executeQuery();
    while (rs.next()) {
    String code = rs.getString("code");
    String name = rs.getString("name");
    Area a = new Area(code, name);
    areas.add(a);
    }
    } catch (Exception e) {
    throw new RuntimeException(e);
    } finally {
    if (conn != null) {
    try {
    conn.close();
    } catch (SQLException e) {
    throw new RuntimeException(e);
    }
    }
    if (ps != null) {
    try {
    conn.close();
    } catch (SQLException e) {
    throw new RuntimeException(e);
    }
    }
    if (rs != null) {
    try {
    conn.close();
    } catch (SQLException e) {
    throw new RuntimeException(e);
    }
    }
    }
    response.setContentType("text/html;charset=UTF-8");
    String json = JSON.toJSONString(areas);
    response.getWriter().print(json);
    }
    }

搜索联想 自动补全

搜索联想和自动补全功能,是页面局部刷新效果,需要使用 AJAX 请求来完成

核心原理

  • 当键盘事件发生之后,发送 AJAX 请求,请求中提交用户输入的内容
  • 后端接收到 AJAX 请求,执行 select 语句进行模糊查询,返回查询结果
  • 将查询结果封装成 json 格式的字符串,然后将 json 格式的字符串响应到前端
  • 前端接收到 json 格式的字符串后,解析该字符串,动态展示到页面上

核心代码
前端:autocomplete.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax实现搜索联想和自动补全</title>
<style>
.userInput {
width: 300px;
height: 30px;
font-size: 20px;
padding-left: 5px;
}

.showDataDiv {
width: 310px;
border: 1px solid lightgrey;
display: none;
}

.showDataDiv p {
padding-left: 5px;
margin-top: 2px;
margin-bottom: 2px;
}

.showDataDiv p:hover {
cursor: pointer;
border: 1px solid blue;
}
</style>
</head>
<body>
<script type="text/javascript" src="/a/js/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
$("#keywords").keyup(function () {
let showDataDiv = $("#showDataDiv");
if (this.value === "") {
showDataDiv.css("display", "none")
} else {
$.ajax({
type: "GET",
url: "/a/autocomplete",
data: "keywords=" + $("#keywords").val(),
async: true,
success(jsonString) {
let jsonArr = eval(jsonString);
let html = "";
for (let i = 0; i < jsonArr.length; i++) {
let json = jsonArr[i];
html += "<p onclick='setInput(\"" + json.content + "\")'>" + json.content + "</p>"
}
showDataDiv.html(html);
showDataDiv.css("display", "block");
}
})
}
})
})

function setInput(content) {
$("#keywords").val(content);
$("#showDataDiv").css("display", "none");
}
</script>

<input type="text" class="userInput" id="keywords">
<div class="showDataDiv" id="showDataDiv">
<!-- <p>java教学</p>-->
<!-- <p>java从入门到放弃</p>-->
<!-- <p>java真的卷</p>-->
</div>
</body>
</html>

后端:AutoCompleteServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @author ShameYang
* @date 2023/9/17 18:44
* @description
*/
@WebServlet("/autocomplete")
public class AutoCompleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Content> contents = new ArrayList<>();
String keywords = request.getParameter("keywords");

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select content from t_ajax where content like ?";
ps = conn.prepareStatement(sql);
ps.setString(1, keywords + "%");
rs = ps.executeQuery();
while (rs.next()) {
String content = rs.getString("content");
Content contentObj = new Content(content);
contents.add(contentObj);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(conn, ps, rs);
}

response.setContentType("text/html;charset=UTF-8");
String json = JSON.toJSONString(contents);
response.getWriter().print(json);
}
}