草庐IT

day34-JSON&Ajax02

liyuelian 2023-03-28 原文

JSON&Ajax02

1.Ajax基本介绍

1.1Ajax是什么

  1. AJAX 即“Asynchronous JavaScript And XML”(异步JavaScript和XML)
  2. Ajax 是一种浏览器异步发起请求(指定发哪些数据),局部更新页面的技术
  3. 传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。而使用Ajax,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
  4. 传统的网页(不使用 AJAX),如果没有得到服务器的响应,浏览器程序会处于一个等待挂起的状态,无法执行其他操作,直至得到http响应。

1.2Ajax经典应用场景

  1. 搜索引擎根据用户输入关键字,自动提示检索关键字

  2. 动态加载数据,按需取得数据 [树形菜单,联动菜单]

  3. 改善用户体验 [输入内容前提示,带进度条文件上传]

  4. 电子商务应用 [购物车,邮件订阅]

  5. 访问第三方服务 [访问搜索服务,rss阅读器]

  6. 页面局部刷新

  • Ajax的三个特点
    1. 异步请求
    2. 发送指定数据
    3. 局部刷新

2.Ajax原理示意图

2.1传统的web应用数据通信方式

传统web应用数据通信方式

  1. 浏览器发出http请求(携带数据username=xxx&pwd=xxx)

  2. 服务端接收数据,并处理

  3. 通过http响应,把数据返回给浏览器

缺点:

  1. 表单提交是把该表单的所有数据都提交给服务端,数据量大,没有意义

  2. 在服务端没有响应前,浏览器前端页面处于等待状态,处于一个挂起的状态

  3. 不能进行局部刷新页面,而是刷新整个页面

2.2Ajax数据通信方式

Ajax数据通信方式:

  1. 浏览器发出http请求,使用XMLHttpRequest对象
  2. 服务端接收数据,并处理
  3. 通过http响应,把数据返回给浏览器(返回的数据格式可以多样 如json,xml,普通文本)

优点:

  1. 可以通过XMLHttpReques对象,发送指定数据,数据量小,速度快

  2. XMLHttpReques是异步发送,在服务端没有Http响应前,浏览器不需要等待,用户可以进行其他操作

  3. 可以进行局部刷新,提高了用户体验

3.JavaScript原生Ajax请求

3.1Ajax文档

AJAX - XMLHttpRequest 对象 (w3school.com.cn)

  • onreadystatechange 事件

当请求被发送到服务器时,我们需要执行一些基于响应的任务。

每当 readyState 改变时,就会触发 onreadystatechange 事件。

readyState 属性存有 XMLHttpRequest 的状态信息。

下面是 XMLHttpRequest 对象的三个重要的属性:

在 onreadystatechange 事件中,我们规定当服务器响应已做好被处理的准备时所执行的任务。

3.2应用实例

演示JavaScript发送原生Ajax请求的案例

  1. 在输入框输入用户名

  2. 点击验证用户名,使用ajax方式,服务端验证该用户名是否已经被占用,如果已经被占用,以json格式返回该用户信息

  3. 假定用户名为king,就不可用,其他用户名可以(后面我们接入DB)

  4. 对页面进行局部刷新,显示返回信息

  5. 思考:为什么直接返回用户名是否可用信息?==>为什么返回json格式的字符串?

    可以根据返回的json可以做更多的业务操作。


思路


  1. 首先创建一个新项目,添加web支持(暂时不使用maven)

  2. 在web-inf文件夹下添加lib目录,添加servlet和json的相关jar包

  3. 配置Tomcat服务器

  4. 创建login.html,使用ajax

    大致的流程是:用户点击验证用户名按钮后,操作dom获取填写的用户名,然后创建XMLHttpRequest对象[ajax引擎对象],调用XMLHttpRequest 对象的 open() 和 send() 方法

    • 在open中设置三个参数:1.请求方式、2.请求url(如果是get请求,url需包括请求参数)、3.是否使用异步发送

    • 给XMLHttpRequest对象绑定一个事件onreadystatechange,该事件的触发时机是XMLHttpRequest的readyState状态改变。

      readyState状态详见:AJAX - 服务器响应 (w3school.com.cn)

    • 然后调用send方法真正发送ajax请求(如果是post请求,参数就是在send方法中设置)

    • 根据readyState的状态判断请求已完成且响应已就绪,然后进行业务操作。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <script type="text/javascript">
        window.onload = function () {//页面加载完毕后执行function
            var checkButton = document.getElementById("checkButton");
            checkButton.onclick = function () {
                //1.创建XMLHttpRequest对象[ajax引擎对象]
                var xhr = new XMLHttpRequest();

                //2.准备发送指定数据
                // 2.1获取用户填写的用户名
                var username = document.getElementById("uname").value;

                // 2.2XMLHttpRequest 对象的 open() 和 send() 方法
                // (1)"GET" 请求方式,也可以是post
                // (2)"/ajax/checkUserServlet?username="+username 就是url
                // (3) true表示异步发送
                xhr.open("GET", "/ajax/checkUserServlet?username=" + username, true);

                //2.3在send方法调用前,给XMLHttpRequest对象绑定一个事件==>onreadystatechange
                //每当XMLHttpRequest对象的 readyState 改变时,就会触发 onreadystatechange 事件
                xhr.onreadystatechange = function () {
                    //如果请求已完成,并且响应已经就绪,并且状态码是200
                    if (xhr.readyState == 4 && xhr.status == 200) {
                        //把返回的json数据显示在div上
                        document.getElementById("div1").innerText = xhr.responseText;
                        // console.log("xhr=", xhr);
                        //处理XMLHttpRequest对象中拿到的数据
                        var responseText = xhr.responseText;
                        // console.log("返回的信息= "+responseText);
                        if (responseText != "") {
                            //根据在servlet设置的逻辑,如果返回的数据不是空串,说明该用户名不可用
                            document.getElementById("myres").value = "用户名不可用";
                        } else {
                            document.getElementById("myres").value = "用户名可用";
                        }

                    }
                }
                
                //2.4真正发送ajax请求[底层还是http请求]
                //如果前面open的第一个参数指定的是post请求,那么post的参数在send中指定
                //如果open的第一个参数指定的是get请求,那么send中不需要写任何数据,因为前面已经在url中指定了
                xhr.send();
            }
        }
    </script>
</head>
<body>
<h1>用户注册</h1>
<form action="/ajax/checkUserServlet" method="post">
    用户名字:<input type="text" name="username" id="uname">
    <input type="button" id="checkButton" value="验证用户名">
    <input style="border-width: 0;color: red" type="text" id="myres"><br/><br/>
    用户密码:<input type="password" name="password"><br/><br/>
    电子邮件:<input type="text" name="email"><br/><br/>
    <input type="submit" value="用户注册">
</form>
<h1>返回的 json 数据</h1>
<div id="div1"></div>
</body>
</html>
  1. 在web.xml中配置servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>CheckUserServlet</servlet-name>
        <servlet-class>com.li.ajax.servlet.CheckUserServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CheckUserServlet</servlet-name>
        <url-pattern>/checkUserServlet</url-pattern>
    </servlet-mapping>
</web-app>
  1. CheckUserServlet:如果接收到的用户名为king,就将其信息以json形式返回前端页面,如果是其他名字,就返回空串。
package com.li.ajax.servlet;

import com.google.gson.Gson;
import com.li.ajax.entity.User;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;

public class CheckUserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //System.out.println("CheckUserServlet 被调用...");

        //接收ajax提交的数据
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");//参数名取决于url中的参数名
        System.out.println("uname=" + username);

        //如果用户名为king,就认为是不可用的
        if ("king".equals(username)) {//假定king已经有人使用了
             //后面这个信息是从数据库db来获取的
            User king = new User(100, "king", "6666", "king@qq.com");
            //把king 对象转成 json 格式的字符串
            String strKing = new Gson().toJson(king);
            //返回信息
            response.getWriter().print(strKing);
        } else {
            //如果用户名可用,返回空串即可
            response.getWriter().print("");
        }
    }
}
  1. Javabean,为了完成对象-->json字符串的需求
package com.li.ajax.entity;

/**
 * User类就是一个Javabean/pojo/entity/domain
 */
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private String email;

    public User(Integer id, String name, String pwd, String email) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

3.3练习

需求分析:到数据库中验证用户名是否可用

  1. 点击验证用户名,到数据库中验证用户名是否可用
  2. 自己设计创建数据库ajaxdb,创建users表
  3. 使用ajax方式,服务端验证该用户名是否已经被占用了,若已经被占用,以json格式返回该用户信息
  4. 对页面进行局部刷新,显示返回信息
  5. 提示:[Mysql+JDBC+数据库连接池]

只需在前面的应用实例中进行升级改进,接入DB

项目的整体架构:

3.3.1创建数据库表格

# 创建数据库
CREATE DATABASE ajaxdb;
# 创建users表
CREATE TABLE `user` (
 id INT(11) PRIMARY KEY,
 `username` VARCHAR(32) UNIQUE NOT NULL DEFAULT '',
 pwd CHAR(32) NOT NULL DEFAULT '',
 email VARCHAR(32) NOT NULL DEFAULT ''
)CHARSET utf8 COLLATE utf8_bin ENGINE INNODB;

#插入测试数据
INSERT INTO `user` VALUES(100,"king",MD5('123'),"king@qq.com");
INSERT INTO `user` VALUES(200,"queen",MD5('666'),"queen@qq.com");
INSERT INTO `user` VALUES(300,"princess",MD5('999'),"princess@163.com");

SELECT * FROM `user`;
DESC `user`;
#drop table `user`;

#完成的工作
SELECT COUNT(*) FROM `user` WHERE `username`="king"

3.3.2创建工具类JdbcUtilsByDruid

在项目的lib库下添加德鲁伊,Apache-DBUtils,mysql-connect数据库连接驱动

数据库连接池详见:javase基础-jdbc和数据库连接池

在项目src目录下添加德鲁伊配置文件

#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/ajaxdb?rewriteBatchedStatements=true
username=root
password=123456
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=20
#max wait time (5000 mil seconds)
maxWait=5000

创建德鲁伊工具类JDBCUtilsByDruid

package com.li.ajax.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.sql.*;
import java.util.Properties;

/**
 * 基于Druid数据库连接池的工具类
 */
public class JDBCUtilsByDruid {

    private static DataSource ds;

    //在静态代码块完成ds的初始化
    //静态代码块在加载类的时候只会执行一次,因此数据源也只会初始化一次
    static {
        Properties properties = new Properties();
        try {
            /**
             * 注意,目前我们是javaweb方式启动
             * 在web项目下要得到资源文件,需要使用类加载器,
             * 得到它真正执行的目录下的out/artifacts/项目名/WEB-INF/classes目录中的资源
             */
            properties.load(JDBCUtilsByDruid.class.getClassLoader()
                    .getResourceAsStream("druid.properties"));
            //方式二:也可以使用绝对路径
            //properties.load(new FileInputStream("D:\\IDEA-workspace\\ajax\\src\\druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    //关闭连接(注意:在数据库连接池技术中,close不是真的关闭连接,而是将Connection对象放回连接池中)
    public static void close(ResultSet resultSet, Statement statemenat, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statemenat != null) {
                statemenat.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

3.3.3BaseDAO

package com.li.ajax.dao;

import com.li.ajax.utils.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import java.sql.*;

/**
 * 开发BasicDAO,是其他DAO的父类
 */
public class BaseDAO<T> {//泛型指定具体的类型

    private QueryRunner queryRunner = new QueryRunner();

    //查询单行结果 的通用方法(单行多列)
    public T querySingle(String sql, Class<T> clazz, Object... parameters) {
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            return queryRunner.query(connection, sql, new BeanHandler<T>(clazz), parameters);

        } catch (SQLException e) {
            throw new RuntimeException(e);//将一个编译异常转变为运行异常
        } finally {
            JDBCUtilsByDruid.close(null, null, connection);
        }
    }

    //根据业务需求还可以有多行多列查询,单行多列插叙,单行单列查询等
    //这里不再列举,详见javase基础-满汉楼BasicDAO
}

3.3.4User类

修改3.2中的Javabean--User类

package com.li.ajax.entity;

/**
 * User类就是一个Javabean/pojo/entity/domain
 */
public class User {
    private Integer id;
    private String username;
    private String pwd;
    private String email;

    public User() {//注意,一定要有一个空的构造器,方便底层进行反射!!
    }

    public User(Integer id, String username, String pwd, String email) {
        this.id = id;
        this.username = username;
        this.pwd = pwd;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

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

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", pwd='" + pwd + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

3.3.5UserDAO

package com.li.ajax.dao;

import com.li.ajax.entity.User;

/**
 * 对user表的增删查改
 */
public class UserDAO extends BaseDAO<User> {
    //我们的UserDAO继承了BasicDAO,并指定了User
    //这时我们就可以使用BasicDAO中的方法
}

3.3.6UserService

package com.li.ajax.service;

import com.li.ajax.dao.UserDAO;
import com.li.ajax.entity.User;
import org.testng.annotations.Test;

import java.sql.SQLException;

/**
 * UserService 提供业务方法
 */
public class UserService {
    //添加属性UserDAO,方便操作
    private UserDAO userDAO = new UserDAO();

    //根据用户名返回对应的user对象,如果没有则返回null
    @Test
    public User getUserByName(String username) throws SQLException {
        //使用 User.class 的目的是底层反射创建对象,而反射是调用的无参构造器去创建类,
        // 因此在对应的实体类中一定要有无参构造器!!!
        User user =
                userDAO.querySingle("SELECT * FROM `user` WHERE `username`=?", User.class, username);
        return user;
    }
}

3.3.7CheckUserServlet

package com.li.ajax.servlet;

import com.google.gson.Gson;
import com.li.ajax.entity.User;
import com.li.ajax.service.UserService;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.sql.SQLException;

public class CheckUserServlet extends HttpServlet {
    //定义一个UserService属性
    private UserService userService = new UserService();

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

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //System.out.println("CheckUserServlet 被调用...");

        //接收ajax提交的数据
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");//参数名取决于url中的参数名
        System.out.println("uname=" + username);

        //从数据库中查找有无相同的用户名
        try {
            User userByName = userService.getUserByName(username);
            if (userByName != null) {//说明用户名存在
                // 返回该User对象的json格式的字符串
                String strUser = new Gson().toJson(userByName);
                //给浏览器返回信息
                response.getWriter().print(strUser);
            } else {
                //如果返回了null,说明没有重名的用户
                //用户名可用,返回空串即可
                response.getWriter().print("");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3.3.8login.html

详见3.2应用实例

3.3.9测试

重启Tomcat,在浏览器中访问http://localhost:8080/ajax/login.html

有关day34-JSON&Ajax02的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  5. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  8. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  9. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  10. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

随机推荐