美文网首页
第十四篇学习总结

第十四篇学习总结

作者: 拼搏男孩 | 来源:发表于2020-01-31 11:49 被阅读0次

第十四篇学习总结

一、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;
    }
}

3、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访问和当前页面不同源的服务器

浏览器之所以禁止js访问和当前页面不同源的服务器,是因为浏览器发送到服务器的请求会默认携带cookie信息,如果用户访问了恶意页面,恶意页面中会有Ajax悄悄的访问用户

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

无论服务器是否允许跨域请求,浏览器都能接收到返回的数据,只是如果服务器不允许的话,浏览器是不会将数据交给发起请求的js代码的

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 {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    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);


    }

    @Override
    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>
    <!-- 将用户信息放在表单中,便于用户修改个人信息 -->
    <form action="/user/editUserInfo">
        <!--<input type="hidden" value="${userInfo.id}" name="id">-->
        头像 :
        <!-- 如果用户没有头像显示一个默认头像 -->
            <c:if  test="${userInfo.icon==null}">
                <img id="img" onclick="openFile()" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1578898078047&di=ec0f9cae6ae4f21bf4fdaaf14644c26f&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201802%2F20%2F20180220165946_RiGPS.thumb.700_0.jpeg">
            </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">
        <br>
        昵称 : <input name="nickName" value="${user.nickName}"> <br>
        真实姓名 : <input name="realName" value="${userInfo.realName}">  <br>
        性别 : <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>
              > 女 <br>
        邮箱 : <input name="email" value="${userInfo.email}">           <br>
        电话 : <input name="phone" value="${userInfo.phone}">           <br>
        住址 : <input name="address" value="${userInfo.address}">        <br>
        <input type="submit">
    </form>

    <hr>

    <div id="addresses">


    </div>

</body>

2、修改用户信息

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

<script>
    //预加载
    $(function () {

        getAddresses();


    })

    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 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 = "<input value='"+item.content+"'></input> <a href='javascirpt:void(0)' onclick='deleteAddressById("+item.id+")'>删除</a>"
                        $("#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拼接

public class ShopDao {

    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 ";
            //如果有商品名称传递过来, 拼接到sql中
            if(goods.getName()!=null){
                sql += " and name like ?";
                objects.add("%"+goods.getName()+"%");
            }
            //如果有类型id传递过来,拼接到sql中,注意, 类型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());
            }

            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;
    }

    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;
    }
}

五、商品排序

用户在购买商品的时候,应该可以进行价格排序与数量排序,既可以正序,也可以倒序,想要解决这个问题就需要在JavaBean中再设置两个字段,这两个字段是字符串用来记录价格排序与数量排序。根据前台发送过来的数据将这两个字符串放进SQL语句中。

import com.qianfeng.utils.BaseBean;

import java.sql.Timestamp;
import java.util.Arrays;

public class Goods extends BaseBean {
    private int id;
    private int userId;
    private String name;
    private double price;
    private int num;
    private String img;
    private String content;
    private Timestamp createTime;
    private int typeId;
    private int status;

    private int[] typeIds;
    private String typeName;
    private String userName;
    private String priceOrder;//用来记录价格排序
    private String numOrder; //用来记录数量排序
    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 ";
            //如果有商品名称传递过来, 拼接到sql中
            if(goods.getName()!=null){
                sql += " and name like ?";
                objects.add("%"+goods.getName()+"%");
            }
            //如果有类型id传递过来,拼接到sql中,注意, 类型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; //表示是否已经拼接的 order by 关键字 true: 没有拼接 false: 已经拼接
            //拼接排序的sql语句
            if(goods.getPriceOrder()!=null&&!goods.getPriceOrder().equals("")){
                //如果order为true,说明没有拼接过order by, 应该拼接上
                if(order){
                    sql += " order by ";
                    order = false;
                }else{ //否则,说明order by已经拼接过了, 而且order by 后面应该有排序方式, 为了拼接当前排序方式, 需要先拼接一个 逗号
                    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;
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="/resource/pages/header.jsp"%>
<html>
<head>
    <title>商城首页</title>
    <style>
        .goods img {
            width: 251px;
            height: 200px;
        }
        .goods{
            display: inline-block;
        }
        .paging {
            text-align: center;

        }
        .paging a{
            margin: 0px 10px;
        }
    </style>
</head>
<body>

<div>
    <form action="/shop/toMainPage" id="form">
        <input id="search" name="name" value="${goods.name}"> <button>搜索</button>  <br>
        类型 :
        <c:forEach items="${types}" var="item">
            <%--form表单提交参数的时候,可以一个参数对应多个值--%>
            ${item.name}<input type="checkbox" name="typeIds" value="${item.id}" onclick="cc()"
        <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 onclick="priceOrder('price ASC')">价格排序</button>
    </c:if>
    <c:if test="${goods.priceOrder eq 'price ASC'}">
        <button onclick="priceOrder('price DESC')">价格正序</button>
    </c:if>
    <c:if test="${goods.priceOrder eq 'price DESC'}">
        <button onclick="priceOrder('price ASC')">价格倒序</button>
    </c:if>

    <c:if test="${empty goods.numOrder}">
        <button onclick="numOrder('num ASC')">数量排序</button>
    </c:if>
    <c:if test="${goods.numOrder eq 'num ASC'}">
        <button onclick="numOrder('num DESC')">数量正序</button>
    </c:if>
    <c:if test="${goods.numOrder eq 'num DESC'}">
        <button onclick="numOrder('num ASC')">数量倒序</button>
    </c:if>
</div>

<div>
    <c:forEach items="${list}" var="item">
        <div class="goods">
            <img src="${item.img}" onclick="toGoodsInfo(${item.id})">
            <div>${item.name}</div>
            <div>${item.price}</div>
            <div>${item.num}</div>
            <button onclick="addCart(${item.id})">加入购物车</button>
        </div>
    </c:forEach>
    <div >${paging}</div>
</div>


</body>
<script>
    function toGoodsInfo(goodsId){
        location.href = "/shop/toGoodsInfoPage?id="+goodsId;
    }


    //点击排序按钮,将字符串值存入到form表单中的input框中,提交form表单
    function priceOrder(po){
        $("#priceOrder").val(po);
        $("#form").submit();
    }
    function numOrder(po){
        $("#numOrder").val(po);
        $("#form").submit();
    }


    function cc(){
        $("#form").submit();
    }

</script>
</html>

相关文章

网友评论

      本文标题:第十四篇学习总结

      本文链接:https://www.haomeiwen.com/subject/kmlgthtx.html