[手把手教程][JavaWeb]优雅的SpringMvc+Mybatis应用(四)

优雅的SpringMvc+Mybatis应用(四)

转眼间文章已经到了第四期了。坚持做一件事,确实是很难的。特别是要不断的转换思维,一个习惯前端开发的人,做什么还是前端的考虑的多一点,后端的架构设计之类的,现在还谈不上,一切稳稳的前进就行了。

关于上一期,本来是投了首页的,后来不知道什么原因没上,检查了一下,也就是推荐了下自己的博客和github,有点惆怅。

工具

  • IDE为idea15
  • JDK环境为1.8
  • maven版本为maven3
  • Mysql版本为5.5.27
  • Tomcat版本为7.0.52
  • 流程图绘制(xmind)

本期目标

  • 登录注册的简单体验优化
  • 完整的后台主页
  • 前端使用json数据
  • 列表数据分页

注册登录的简单体验优化

上一期我们注册登录都成功了,但是后台主页显示很丑陋,所以这里我换了个主页,但是前面没有注意到的细节又看到了。如下图:

[手把手教程][JavaWeb]优雅的SpringMvc+Mybatis应用(四)_第1张图片
ssm应用四-注册成功-地址栏错误

在上面的图中,地址栏显示的地址是前面注册接口的地址,并不是我们常规看到的xxx/home这种主页地址。所以我们需要进行优化处理。

同时,我们可以看到我们的Form表单提交的提示信息是在新产生的ModelAndView界面里面addObject("字段名",数据),这样我们的数据都显示到新的界面去了。也就是说前面的设计不合符现在主流的开发思路,用户体验也相对糟糕。我们需要做到在登录界面前端效验数据,同时登录注册的提示信息也是在对应的界面完成的。如下:

[手把手教程][JavaWeb]优雅的SpringMvc+Mybatis应用(四)_第2张图片
ssm应用四-前端form错误-提示示例

值得注意的是:为了程序执行效率、数据完整性和程序健壮性,我们的前端必须做对应的基础数据效验,后端的控制器必须做所有需要的数据的效验。

  • 前端数据效验我们使用js完成
  • 前端界面样式是由CSS完成
  • 网络请求采用异步请求,具体的实现是使用的ajax完成
  • js获取web页面数据统一使用标签的ID,格式为:$("#标签ID")
  • web页面标签最好一个标签一行,这样代码看起来更加舒服

我们先重构登录页面:
首先,我不擅长写web页面,我能做的也就是少量的修改,CSS和js本身不是我的强项,需要大量的时间来磨合。

所以,我选择了在网上找web界面,然后自己做少量的修改,同时一些简单的小控件我也从网络获取资源来解决需要,合理的查找资源是最快的学习方法

登录页面重构目标:

  • web前端完成基本的数据效验
  • 数据效验完成后,有基本的对应提示。如上面登录界面的小标签。
  • 异步登录
  • 后端接口返回数据为json
  • 前端页面解析json控制程序流转

首先,按照上面的提示,我们可以知道的是前端页面上面的基本数据效验是要使用js完成的,同时js中获取web页面标签的数据是需要使用标签的ID完成,简单的示例如下:


上面的注释已经能很明显的看出我们的 前端效验、网络请求和js解析json,下面我们在前端页面中调用这个js,如下:

上面就是web中调用js的简单实现,注意的是,FORM表单必须删除action的值,在点击后需要触发对应事件的地方调用js

当然,我们的前端页面完成后,我们必须在后端接口处,做出对应的修改,让他符合我们前端的调用规则。后端修改如下:

/**
 * 用户请求相关控制器
 * 
Created by acheng on 2016/9/26. */ @Controller //标明本类是控制器 @RequestMapping("/userAction") //外层地址 public class UserController { @Autowired private UserServiceImpl userService; //自动载入Service对象 private ResponseObj responseObj; //返回json数据的实体 /** * 登录接口,因为json数据外层一般都是Object类型,所以返回值必须是Object
* 这里的地址是: 域名/userAction/login * * @param req * @param user * @return */ @RequestMapping(value = "/login" //内层地址 , method = RequestMethod.POST //限定请求方式 , produces = "application/json; charset=utf-8") //设置返回值是json数据类型 @ResponseBody public Object login(HttpServletRequest req, User user) { Object result; if (null == user) { responseObj = new ResponseObj(); responseObj.setCode(ResponseObj.EMPUTY); responseObj.setMsg("登录信息不能为空"); result = new GsonUtils().toJson(responseObj); //通过gson把java bean转换为json return result; //返回json } if (StringUtils.isEmpty(user.getLoginId()) || StringUtils.isEmpty(user.getPwd())) { responseObj = new ResponseObj(); responseObj.setCode(ResponseObj.FAILED); responseObj.setMsg("用户名或密码不能为空"); result = new GsonUtils().toJson(responseObj); return result; } //查找用户 User user1 = userService.findUser(user); if (null == user1) { responseObj = new ResponseObj(); responseObj.setCode(ResponseObj.EMPUTY); responseObj.setMsg("未找到该用户"); result = new GsonUtils().toJson(responseObj); } else { if (user.getPwd().equals(user1.getPwd())) { responseObj = new ResponseObj(); responseObj.setCode(ResponseObj.OK); //登录成功,状态为1 responseObj.setMsg(ResponseObj.OK_STR); responseObj.setData(user1); //登陆成功后返回用户信息 result = new GsonUtils().toJson(responseObj); } else { responseObj = new ResponseObj(); responseObj.setCode(ResponseObj.FAILED); responseObj.setMsg("用户密码错误"); result = new GsonUtils().toJson(responseObj); } } return result; } }

注意:如果为了返回数据为json,那么我们需要设定某个方法对应的注解为:@ResponseBody 。 否则会产生404错误

我们通过上面的重构可以明白以下几点:

  • 前端
    • js实现基本的数据效验
    • js发起网络请求
    • ajax发起网络请求,返回类型设置json能自动解析
    • js获取页面控件
    • 页面控件调用js
    • js获取解析后的json数据的值,进行程序流转控制
  • 后端:
    • 后端控制器必须申明
    • 后端的地址必须配置
    • 每个地址返回的数据类型要匹配
    • 返回json数据,方法上面必须配置:@ResponseBody
    • 可以使用工具类来方便开发

后台主页→个人信息修改

上期我们可以看到,我们的登录和注册都是已经OK了。现在我们登录和注册成功后,我们都让他跳转到主页去。同时完善登录和注册的错误提示页面。

一般来说,大家更喜欢看到登录成功后的主页界面,毕竟大多数人都是有喜新厌旧之嫌。我也是一样的。哈哈。

为了程序的执行逻辑,考虑后端需求都不是那么单一,我们先做一些公共的建设。比如说用户信息修改现实之类的。如下图:

[手把手教程][JavaWeb]优雅的SpringMvc+Mybatis应用(四)_第3张图片
ssm应用四-后台主页-修改个人信息

如上图所示,我们需要一个可以弹出的对话框,我去百度了一下,那个“妹子UI”还是很受人欢迎,所以就集成进来了。

我们选取一个比较喜欢的后端主页,然后把对应的资源放入到对应的文件目录(js、css、images等),需要新加入的资源如果在以前的目录中没有的话,那么我们需要在里面进行配置。比如说这里我加入了字体文件,那么我现在需要先把字体文件指定目录为:

static/font/

目录指定后我需要在Spring的配置文件,spring-web.xml中配置静态资源的目录如下:


剩下的就是写好jsp页面(Copy+Pause+修改资源文件路径)。然后我们在Controller中配置好路径

/**
 * 后台主页
 *
 * @return
 */
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home() {
    return "home";
}

这样子配置好了后,我们就可以直接用“域名/mvc/home”来访问我们的主页了。同时按照上面的设置,我们登录成功后,直接解析json确认用户登录成功,然后前端使用js来进行页面跳转,如:

window.location.href = "<%=request.getContextPath()%>/mvc/home";    //跳转到主页

这样,我们就能修复上面说道的页面和地址显示不匹配的问题。

同时,通过上面的可以看出,我们在jsp页面中,纯粹没加入任何jsva代码,全是使用的前端+接口实现的功能。我们这样做,以后维护和重构中也能降低一部分压力。

言归正传,我们这里主要是想做一个个人信息修改的功能。首先我们进行功能和业务流程分析。

功能和业务流程分析:

  • 1.web点击头像显示修改信息对话框。
  • 2.根据后端定义的用户信息表,得出用户信息修改需要填写的资料。
  • 3.用户上传个人资料,上传之前前端必须先进行基础信息验证。
  • 4.用户个人信息验证通过后,上传到服务器。(重点)
  • 5.服务器接收上传的信息,进行存储,并返回修改结果。(重点)

从上面我们可以看到我画出两个重点,而且这两个重点都是java web避免不了事情。为什么这样说呢?

  • 1.任何一个动态的web服务器都免不了数据资料的更新,数据资料更新一般分为两种。
    • 有文件的信息上传
    • 无文件的信息上传
  • 2.可能其他童鞋看到http请求的方法有很多种,但是一般来说get和post我们能做出任何的操作。
  • 3.在大量数据的服务器中,考虑到很多因素(历史记录查询、数据库增量等),一般不会进行真正的物理数据删除,一般都是通过控制输出来实现的。(实战经验,血泪教训,切记)

现在我们开始实现对话框:

打开“妹子UI”的js插件页面,我们找到模态窗口相关的文档,在“模拟 Prompt”这里,我们可以看到具体的对话框的实现和调用如下:




Amaze UI
来来来,吐槽点啥吧
$(function() { $('#doc-prompt-toggle').on('click', function() { //在这里设定上面的按钮的点击函数 $('#my-prompt').modal({ //显示ID为my-prompt的窗口 relatedTarget: this, onConfirm: function(e) { //窗口的确定按钮的响应事件 alert('你输入的是:' + e.data || '') }, onCancel: function(e) { //取消按钮的响应事件 alert('不想说!'); } }); }); });

关于上面的相关代码,我们需要引入妹子UI后才能使用!!!接下来我们需要改造成符合我们实际需求的界面,如下:


用户信息修改
姓名: 性别: 手机号: 年龄: 头像:
var fileName; function uploadFile() { //这里应该加入Loading 窗口开启 fileName = document.getElementById('changeHeadPic').value; $.ajaxFileUpload({ url: "<%=request.getContextPath()%>/userAction/uploadHeadPic", secureuri: false, //是否需要安全协议,一般设置为false fileElementId: 'changeHeadPic', //文件上传域的ID dataType: 'json', //返回值类型 一般设置为json contentType: "application/x-www-form-urlencoded; charset=utf-8", success: function (data) { alert(data.msg); //先根据返回的code确定文件是否上传成功 //文件上传失败,直接弹出错误提示,根据错误进行相应的事物处理(关闭Loading窗口,弹出提示对话框) //文件上传成功后,继续现实loading窗口,接着执行上传表单信息等事物 } }); } function changeUserInfo() { //显示个人信息修改窗口 $('#my-prompt').modal({ relatedTarget: this, onConfirm: function () { uploadFile(); //调用上面的文件上传函数 }, onCancel: function (e) { } }); }

上面的代码,我们完成了控制窗口显示的函数,完成了修改个人信息界面的构建。现在我们需要的是找到执行程序入口。按照我的习惯,肯定是找到头像控件,然后设置点击事件为上面的changeUserInfo()。实现如下:


user-img

好的,上面我们可以看到我的前端界面代码基本上完成了。接下来,我们需要在我们后端上面写上对应的程序接口,实现功能即可。

本来计划文件上传单独使用commons-fileupload和commons-io完成的,毕竟这是在Servelt上面的老套路,但是我发现Spring里面已经考虑到这一点,有新的东西来完成,所以就使用了Spring的实现方式。具体代码如下:

    //我们在UserController这个控制器里添加这个方法
    @RequestMapping(value = "/uploadHeadPic"
            , method = RequestMethod.POST
            , produces = "application/json; charset=utf-8")
    @ResponseBody
    public Object uploadHeadPic(@RequestParam(required = false) MultipartFile file, HttpServletRequest request) {
        //在这里面文件存储的方案一般是:收到文件→获取文件名→在本地存储目录建立防重名文件→写入文件→返回成功信息
        //如果上面的步骤中在结束前任意一步失败,那就直接失败了。
        if (null == file || file.isEmpty()) {
            responseObj = new ResponseObj();
            responseObj.setCode(ResponseObj.FAILED);
            responseObj.setMsg("文件不能为空");
            return new GsonUtils().toJson(responseObj);
        }
        responseObj = new ResponseObj();
        responseObj.setCode(ResponseObj.OK);
        responseObj.setMsg("文件长度为:" + file.getSize());
        return new GsonUtils().toJson(responseObj);
    }

完成了上面的方法后,我们觉得应该是没问题了,毕竟这样一个接口来接受请求是没问题的嘛,是的,我也是这么认为的。

但是现实的打脸是很严重的,因为按照这么写后,我无论如何都收不到文件(文件一直为null),Why?我的思路是正确的啊。经过我的仔细查找,发现我的Spring的配置文件中,没有添加文件的支持设置,所以我们又得补充配置文件,spring-web.xml新增配置如下:

    
        
           
    

经过上面的一番折腾,我们发现框架这个东西,也不是一劳永逸的,毕竟很多东西需要不断的增加。

总结:

  • 任何东西都需要根据需求不断的变化,可增可减,张弛有度。
  • Spring接收文件上传时,Controller的具体方法的参数前面插入注解,同时数据类型是MultipartFile。
  • 引入第三方资源的时候,必须查看文档,根据说明文档好办事。
  • js进行前端流程控制,后端接口隔离,前后端解耦。

前两天刮台风,停电导致数据丢失,是个很尴尬的事情,拖慢了进度。同时,朋友遇到点问题,我在开导他。文章写到现在也是凌晨4点过了,本期计划的列表分页也没做,很对不起大家对我的期待。真诚的说一声:对不起。对不起你们的期待。

给朋友开导的时候,我也总结了下做人做事:随心、追梦、勇敢、独行。希望有心做事的,都用这几句话勉励自己吧。

前行的路上不只是孤独,还有满山的鲜花,更有远方和诗。


下期预告:

  • 列表分页
  • 简易用户角色控制
  • 拦截器的使用

你可能感兴趣的:([手把手教程][JavaWeb]优雅的SpringMvc+Mybatis应用(四))