Java - Part 14

一、Ajax

  • Ajax可以让浏览器在不刷新页面的情况下。利用js代码和后台进行交互,这种交互方式称之为异步。

1、原生js的Ajax

//获取异步对象
var ajax = new XMLHttpRequest();
//设置数据, method: 请求模式   url: 地址 arsc: 是否异步
ajax.open(method,url,async);
//data: 数据
ajax.send(data);
//定义个请求过程中每个状态变化时的回调函数
ajax.onreadystatechange = function () {
    //当请求进行到第四阶段,也就是有数据返回时,和返回的状态值是200时
    if(ajax.readyState==4&&ajax.status==200){
        //获取返回的数据
        var resp = ajax.responseText;
    }
}

2、jQuery的Ajax

$.ajax({
    url:"请求地址",
    type:"请求模式",
    data:{"数据名":"数据值"},//js对象
    contentType:"application/json",//默认"application/x-www-form-urlencoded"
    dataType:"json",//默认是 text  , js 会按照预制的数据格式解析数据
    success:function(data){ //请求成功返回之后调用的方法 data:返回的数据    
    }
})

二、跨域解决方案CORS

1、同源策略

  • 同源:ip地址+端口号相同
  • 浏览器禁止js访问和当前页面不同源的服务器

2、跨域

  • 有的时候,我们自己的两个服务器之间需要相互访问,这时就必须要跨域了,比如说图片服务器和本地服务器。要解决跨域问题,就需要浏览器和服务器相互配合,浏览器发出跨域请求,服务器允许跨域请求

3、两种请求:简单请求与非简单请求

  • 3.1 简单请求
    符合简单请求必须满足以下两个条件:
    • 请求方法是head、get、post这三种方法之一
    • content-type只限于三种类型:application/x-www-form-urlencoded、multipart/form-data、text/plain
    • 不自定义字段
  • 3.2 简单请求基本流程
    • 浏览器如果发现是跨域请求, 就会在请求头中添加Origin字段,该字段的值为当前域名
    • 服务器收到请求后,检查这个字段的值, 判断是否允许这个域名下的请求跨域进来
    • 如果允许的话, 就需要在响应中返回特殊字段, 字段的值要设置为允许请求的域名
    • 当浏览器接受到请求,检查服务返回的特殊字段,如果这个特殊字段中记录的值是符合当前域名, 才会将返回的数据传递给js的接收方法,否则就报错

响应中的特殊字段:

  • Access-Control-Allow-Origin
    该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
  • Access-Control-Allow-Credentials
    该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
  • Access-Control-Expose-Headers
    该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
  • withCredentials
    如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段,另一方面,开发者必须在AJAX请求中打开withCredentials属性
    但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials= false
  • 3.3 非简单请求基本流程
    • 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),就请求当前url指向的Servlet中的 OPTIONS方法
    • 如果 OPTIONS 方法返回的信息中没有允许的特殊字段, 浏览器就会拒绝发起正式请求
    • 除了Origin字段,"预检"请求的头信息包括两个特殊字段。
  • Access-Control-Request-Method
    该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT
  • Access-Control-Request-Headers
    该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header
  • 响应字段
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
  • Access-Control-Allow-Methods
    ​该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
  • Access-Control-Allow-Headers
    ​如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
  • Access-Control-Allow-Credentials
    ​该字段与简单请求时的含义相同。
  • Access-Control-Max-Age
    ​该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

4、文件服务器
将文件但粗存放在一个服务器上有很多好处,此时就需要用到跨域请求,解决跨域请求的最佳方式是在过滤器中对特定源的请求允许跨域

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*")
public class CorsFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException { }
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        resp.setHeader("Access-Control-Allow-Origin","http://localhost:8090");//* 表示允许所有
        resp.setHeader("Access-Control-Allow-Methods","post, get");//表示允许post与get请求
        filterChain.doFilter(servletRequest,resp);
    }
    public void destroy() { }
}

三、用户信息的增删改查

1、显示用户详情

在用户详情界面应该展示用户的详细信息,比如用户名、头像、昵称。要完成这些,首先要拿到用户的id,用户的信息在登录的时候就已经存在session中了,可以根据这个来从数据库中查询用户的详细信息,然后放进response中,转发到jsp页面。

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    UserBean userBean = (UserBean) req.getSession().getAttribute("user");
    int id = userBean.getId();
    UserInfo userInfo = userInfoService.getUserInfoByUserId(id);
    req.setAttribute("userInfo",userInfo);
    req.getRequestDispatcher("/WEB-INF/pages/userInfo.jsp").forward(req,resp);
}
<body style="background: url(https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1578736051725&di=76768c9735298e1e0422b1930727c243&imgtype=0&src=http%3A%2F%2Fimg.pptjia.com%2Fimage%2F20190112%2F43847e4d1e0b3af9623f502c45148e15.jpg)">
    <div class="form">
        <%--个人信息--%>
        <div id="content">
            <form action="/user/editUserInfo">
                <div class="heading"><h2>个人信息中心</h2></div>
                <table class="table">
                    <tr>
                        <th>头&ensp;像:</th>
                        <td>
                            <c:if test="${userInfo.icon == null}">
                                <img id="img" onclick="openFile()" src="http://office-1256119282.file.myqcloud.com/20190730/office-cn/official-web/static/img/ic_logo@2x.png">
                            </c:if>
                            <c:if test="${userInfo.icon != null}">
                                <img id="img" src="${userInfo.icon}" onclick="openFile()">
                            </c:if>
                            <input type="hidden" name="icon" value="${userInfo.icon}" id="icon">
                            <input type="file" id="file" onchange="upload()" style="display: none">
                        </td>
                    </tr>
                    <tr>
                        <th>昵&ensp;称:</th>
                        <td><input class="inp" name="nickName" value="${user.nickName}"></td>
                    </tr>
                    <tr>
                        <th>真实姓名:</th>
                        <td><input class="inp" name="realName" value="${userInfo.realName}"></td>
                    </tr>
                    <tr>
                        <th>性&ensp;别:</th>
                        <td>
                            <input type="radio" name="sex" value="1"
                            <c:if test="${userInfo.sex==1}"> checked="checked" </c:if> > 男
                            <input type="radio" name="sex" value="2"
                            <c:if test="${userInfo.sex==2}"> checked="checked" </c:if> > 女
                        </td>
                    </tr>
                    <tr>
                        <th>邮&ensp;箱:</th>
                        <td><input class="inp" name="email" value="${userInfo.email}"></td>
                    </tr>
                    <tr>
                        <th>电&ensp;话:</th>
                        <td><input class="inp" name="phone" value="${userInfo.phone}"></td>
                    </tr>
                    <tr>
                        <th>住&ensp;址:</th>
                        <td><input class="inp" name="address" value="${userInfo.address}"></td>
                    </tr>
                    <tr>
                        <td colspan="2">
                            <input type="submit" class="btn" value=修改个人信息>
                            <a href="/shop/toMainPage">返&ensp;回</a>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
        <hr><hr>
        <div id="addresses">
            <a href="javascript:void(0)" onclick="addAddress()">添&ensp;加</a><br><br>
        </div>
        <%--添加--%>
        <div class="form-group" id="add" style="display: none">
            <table class="table">
                <tr><td colspan="2"><div class="heading">添加地址</div></td></tr>
                <tr>
                    <td>收货地址:</td>
                    <td><input class="inp" id="addAddress"></td>
                </tr>
                <tr>
                    <td colspan="2">
                        <button class="btn" onclick="addAddressById()">添&ensp;加</button>
                        &ensp;&ensp;&ensp;&ensp;
                        <button class="btn" onclick="cancel()">取&ensp;消</button>
                    </td>
                </tr>
            </table>
        </div><br>
    </div>
<br><br>
</body>

2、修改用户信息

我们在用户信息详情页面已经展示了用户信息。还提供了修改用户信息的操作,用户点击修改按钮就会触发Ajax操作与数据库后台进行交互,完成用户信息的修改,修改完成之后通过Ajax重新请求当前页面。

<script>
    //预加载
    $(function () {
        getAddresses();
    });
    //取消添加
    function cancel(){
        $("#content").show(1000);
        $("#add").hide(1000);
    }
    //打开添加框
    function addAddress(){
        $("#content").hide(1000);
        $("#add").show(1000);
    }
    //删除地址
    function deleteAddressById(id) {
        $.ajax({
            url:"/user/deleteAddressById",
            type:"post",
            data:{id:id},
            dataType:"json",
            success:function (data) {
                if(data.code==-1) {
                    alert(data.message);
                } else {
                    $("#addresses").empty();
                    getAddresses();
                }
            }
        });
    }
    //修改地址
    function editAddressById(id) {
        var content = $("#address").val();;
        $.ajax({
            url:"/user/editAddressById",
            type:"post",
            data:{id:id,content:content},
            dataType:"json",
            success:function (data) {
                if(data.code==-1) {
                    alert(data.message);
                } else {
                    $("#addresses").empty();
                    getAddresses();
                    location.reload();
                }
            }
        });
    }
    //添加地址
    function addAddressById() {
        var content = $("#addAddress").val();
        $.ajax({
            url:"/user/addAddressById",
            type:"post",
            data:{content:content},
            dataType:"json",
            success:function (data) {
                if(data.code==-1) {
                    alert(data.message);
                } else {
                    $("#addresses").empty();
                    getAddresses();
                    location.reload();
                }
            }
        });
    }
    //获取用的所有收货地址
    function getAddresses() {
        $.ajax({
            url:"/user/getAddress",
            type:"post",
            dataType:"json",
            success:function (data) {
                if(data.code == -1) {
                    alert(data.message);
                } else {
                    var list = data.data;
                    list.forEach(function(item) {
                        var html = "<br><input id='address' class='inp' value='"+item.content+"'> &ensp;&ensp;&ensp;&ensp;" +
                            "<a href='javascript:void(0)' onclick='deleteAddressById("+item.id+")'>删&ensp;除</a>&ensp;" +
                            "<a href='javascript:void(0)' onclick='editAddressById("+item.id+",this)'>修&ensp;改</a>&ensp;<br><br>";
                        $("#addresses").append(html);
                    });
                }
            }
        });
    }
    //打开文件并上传
    function openFile() {
        $("#file").click();
    }
    function upload() {
        var formData = new FormData();
        formData.append("file",$("#file")[0].files[0]);
        $.ajax({
            url:"http://localhost:8070/FileServer/file/upload",
            type:"post",
            data:formData,
            dataType:"json",
            contentType:false,
            processData: false,
            success:function(data) {
                if(data.errno == 0) {
                    alert("上传成功");
                    $("#icon").val(data.data[0]);
                    $("#img").attr("src",data.data[0]);
                } else {
                    alert("上传失败");
                }
            }
        });
    }
</script>

四、商品条件筛选与排序

  • 应当提供商品名称的搜索以及商品类型的筛选,但是有一个问题,商品类型这个参数用户可以不选,可以选一个,也可以选多个,处理这种问题需要SQL拼接
  • 用户在购买商品的时候,应该可以进行价格排序与数量排序,既可以正序,也可以倒序,想要解决这个问题就需要在JavaBean中再设置两个字段,这两个字段是字符串用来记录价格排序与数量排序。根据前台发送过来的数据将这两个字符串放进SQL语句中。
package com.shop.dao;

import com.shop.bean.Goods;
import com.utils.DBUtils;
import com.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class ShopDao {
    /**
     * 根据id查找商品详情信息
     * @param goods
     * @return
     */
    public Goods getGoodsById(Goods goods) {
        Connection cn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            cn = JDBCUtils.getConnection();
            ps = cn.prepareStatement("select g.*,t.name as typeName from goods g left join types t on t.id = g.typeId where g.id = ?");
            ps.setInt(1,goods.getId());
            rs = ps.executeQuery();
            Goods goodsBean = DBUtils.selectOne(Goods.class,rs);
            return goodsBean;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.close(rs,ps,cn);
        }
        return null;
    }
    /**
     * 获取全部上架商品
     * @param goods
     * @return
     */
    public List<Goods> getGoods(Goods goods){
        Connection cn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            cn = JDBCUtils.getConnection();
            //拼接预制型sql语句
            //sql语句中每拼接一个问号, 要往数组中存入一个对应的值,
            //总共有几个问号,对应的值的顺序是正确的
            List<Object> objects = new ArrayList<>();
            //sql语句
            String sql = "select * from goods where status=1 ";
            //有商品名称, 拼接
            if(goods.getName()!=null){
                sql += " and name like ? ";
                objects.add("%"+goods.getName()+"%");
            }
            //有类型id,拼接
            if(goods.getTypeIds()!=null){
                sql += " and typeId in (" ;
                int[] typeIds = goods.getTypeIds();
                //遍历类型id
                for (int i = 0; i < typeIds.length; i++) {
                    if(i==typeIds.length-1){
                        sql += "? ) ";
                    }else{
                        sql += "? , ";
                    }
                }
                objects.add(goods.getTypeIds());
            }
            //排序
            boolean order = true;
            if (goods.getPriceOrder()!=null&&!goods.getPriceOrder().equals("")){
                if (order){
                    sql += " order by ";
                    order = false;
                } else {
                    sql += " , ";
                }
                sql += goods.getPriceOrder();
            }
            if (goods.getNumOrder()!=null&&!goods.getNumOrder().equals("")){
                if (order){
                    sql += " order by ";
                    order = false;
                } else {
                    sql += " , ";
                }
                sql += goods.getNumOrder();
            }
            ps = cn.prepareStatement(sql+" limit ? , ?");
            //设置sql语句对应的参数值
            int n = 0; //记录已经设置了多少个参数值
            for (Object object : objects) {
                //拿到的值是一个数组, 需要转换并遍历
                if(object.getClass().isArray()){
                    int[] typeIds = (int[]) object;
                    for (int i = 0; i <typeIds.length; i++) {
                        n++;
                        ps.setObject(n,typeIds[i]);
                    }
                }else{
                    n++;
                    ps.setObject(n,object);
                }
            }
            ps.setInt(n+1,goods.getStart());
            ps.setInt(n+2,goods.getPageSize());
            rs = ps.executeQuery();
            List<Goods> list = DBUtils.selectMore(Goods.class,rs);
            return list;
        }catch (Exception  e){
            e.printStackTrace();
        }finally {
            JDBCUtils.close(rs,ps,cn);
        }
        return null;
    }
    /**
     * 获取上架商品数量
     * @param goods
     * @return
     */
    public int getGoodsCount(Goods goods){
        Connection cn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            cn = JDBCUtils.getConnection();
            List<Object> objects = new ArrayList<>();
            String sql = "select count(1) from goods where status=1 ";
            if(goods.getName()!=null){
                sql += " and name like ?";
                objects.add("%"+goods.getName()+"%");
            }
            if(goods.getTypeIds()!=null){
                sql += " and typeId in (" ;
                int[] typeIds = goods.getTypeIds();
                for (int i = 0; i < typeIds.length; i++) {
                    if(i==typeIds.length-1){
                        sql += "? )";
                    }else{
                        sql += "? , ";
                    }
                }
                objects.add(goods.getTypeIds());
            }
            ps = cn.prepareStatement(sql);
            int n = 0;
            for (Object object : objects) {
                if(object.getClass().isArray()){
                    int[] typeIds = (int[]) object;
                    for (int i = 0; i <typeIds.length; i++) {
                        n++;
                        ps.setObject(n,typeIds[i]);
                    }
                }else{
                    n++;
                    ps.setObject(n,object);
                }
            }
            rs = ps.executeQuery();
            if(rs.next()){
                return rs.getInt(1);
            }
        }catch (Exception  e){
            e.printStackTrace();
        }finally {
            JDBCUtils.close(rs,ps,cn);
        }
        return 0;
    }
}
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="/resource/pages/header.jsp"%>
<html>
<head>
    <title>商城首页</title>
    <style>
        body { font-family: 楷体; }
        div { text-align: center; }
        .goods{ display: inline-block; }
        img {
            width: 200px;
            height: 200px;
        }
    </style>
    <link rel="stylesheet" href="/resource/css/MyCss.css">
</head>
<body style="background: url(https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1578736051725&di=76768c9735298e1e0422b1930727c243&imgtype=0&src=http%3A%2F%2Fimg.pptjia.com%2Fimage%2F20190112%2F43847e4d1e0b3af9623f502c45148e15.jpg)">
    <div class="form">
        <div class="heading">
            <h3>商城商品页面</h3>
        </div>
        <div class="form-group">
            <form id="form" action="/shop/toMainPage">
                <input id="search" class="inp" name="name" value="${goods.name}"> <button class="btn">搜索</button>
                <br>类型 :
                <c:forEach items="${types}" var="item">
                    ${item.name}
                    <input type="checkbox" name="typeIds" value="${item.id}" onclick="check()"
                    <c:forEach items="${goods.typeIds}" var="typeId">
                           <c:if test="${typeId==item.id}">checked="checked"</c:if>
                    </c:forEach> >
                </c:forEach><br>
                <input type="hidden" value="${goods.priceOrder}" name="priceOrder" id="priceOrder">
                <input type="hidden" value="${goods.numOrder}" name="numOrder" id="numOrder">
            </form>
        </div>
        <div>
            排序:
            <%--价格排序--%>
            <c:if test="${empty goods.priceOrder}">
                <button class="btn" onclick="priceOrder('price ASC')">价格排序</button>
            </c:if>
            <c:if test="${goods.priceOrder eq 'price ASC'}">
                <button class="btn" onclick="priceOrder('price DESC')">价格正序</button>
            </c:if>
            <c:if test="${goods.priceOrder eq 'price DESC'}">
                <button class="btn" onclick="priceOrder('price ASC')">价格倒序</button>
            </c:if>
            <%--数量排序--%>
            <c:if test="${empty goods.numOrder}">
                <button class="btn" onclick="numOrder('num ASC')">数量排序</button>
            </c:if>
            <c:if test="${goods.numOrder eq 'num ASC'}">
                <button class="btn" onclick="numOrder('num DESC')">数量正序</button>
            </c:if>
            <c:if test="${goods.numOrder eq 'num DESC'}">
                <button class="btn" onclick="numOrder('num ASC')">数量倒序</button>
            </c:if>
        </div>
        <div class="form-group">
            <c:forEach items="${list}" var="item">
                <div class="goods">
                    <img src="${item.icon}">
                    <div>${item.name}</div>
                    <div style="color: lawngreen;font-weight: 600;">${item.price}¥</div>
                    <div style="color: blue">${item.num}件</div>
                    <button class="btn" onclick="toGoodsInfo(${item.id})">加入购物车</button>
                </div>
            </c:forEach>
        </div><br>
        <div class="paging">
            ${paging}
        </div>
    </div>
</body>
<script src="/resource/js/jquery.js"></script>
<script>
    function check(){
        $("#form").submit();
    }
    function priceOrder(po){
        $("#priceOrder").val(po);
        $("#form").submit();
    }
    function numOrder(po){
        $("#numOrder").val(po);
        $("#form").submit();
    }
    function toGoodsInfo(goodsId){
        location.href = "/shop/toGoodsInfoPage?id="+goodsId;
    }
</script>
</html>

推荐阅读更多精彩内容

  • 题目1.什么是同源策略? 同源策略(Same origin Policy): 浏览器出于安全方面的考虑,只允许与本...
    FLYSASA阅读 1,204评论 0 6
  • 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实...
    Yaoxue9阅读 876评论 0 6
  • 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实...
    他方l阅读 714评论 0 2
  • 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实...
    HeroXin阅读 537评论 0 3
  • 引用自HTTP访问控制(CORS) 当 Web 资源请求由其它域名或端口提供的资源时,会发起跨域 HTTP 请求(...
    有涯逐无涯阅读 2,197评论 0 4
  • 路仁义让过水长东,一拳打向陆人龙。水长东是尝过这拳法威力的,见状连忙提醒陆人龙:“嘿哥们小心!” ...
    高等巫妖阅读 64评论 0 1
  • 一、时间 时间是什么?有人说“时间是金钱”,有人说“时间是生命”,有人说“时间是友情”,我说时间就是...
    溪山土壤肥料贵阳阅读 87评论 0 1
  • 从个人的角度来看: (今天我的心情怎么样?有什么印象深刻的事情?什么事情让我获得了成就感?遇到了什么问题?有什么疑...
    陈锦楣阅读 54评论 0 1
  • 参考链接http://blog.csdn.net/lby978232/article/details/8887650
    ktide阅读 79评论 0 0