[置顶] 在Spring MVC框架下利用RESTful API和MongoDB实现用户留言与邮件反馈
2017-07-03 18:41
531 查看
在Spring MVC框架下,基于注解映射和数据绑定编写Java业务代码,采用MongoDB数据库进行数据存储,使用JSP和JS渲染表单页面,利用RESTful API实现基于URL的请求处理服务,以实现简单的用户留言与邮件反馈功能。通过IDEA IDE工具搭建好Spring MVC框架并连接MongoDB数据库后,即可进行该功能的实现。
由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller;
DispatcherServlet将请求提交到Controller;
Controller调用业务逻辑处理后,返回ModelAndView;
DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图;
视图负责将结果显示到客户端。
Spring MVC运行流程
执行步骤
客户端请求提交到DispatcherServlet;由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller;
DispatcherServlet将请求提交到Controller;
Controller调用业务逻辑处理后,返回ModelAndView;
DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图;
视图负责将结果显示到客户端。
执行流程图
Domain与Dto对象
Domain对象
/** * UserFeedback.java * * @author Zero */ //@Document>>将domain依赖注入MongoDB Repository @Document(collection = "UserFeedback") //UserFeedback类通过AbstractDomain实现了java.io.Serializable接口,其实例可安全的将数据保存到HttpSession中 public class UserFeedback extends AbstractDomain { private static final long serialVersionUID = 3843558621545635010L; //姓名 private String fullName; //职位 private String position; //电话 private String phone; //留言类型 private FeedbackType contentType; //留言内容 private String content; //留言状态 private FeedbackStatus status; public UserFeedback() { } public String fullName() { return fullName; } public UserFeedback fullName(String fullName) { this.fullName = fullName; return this; } //此处省略其他属性的get/set方法 @Override public String toString() { return "UserFeedback{" + "fullName='" + fullName + '\'' + ", position='" + position + '\'' + ", phone='" + phone + '\'' + ", contentType='" + contentType + '\'' + ", content='" + content + '\'' + ", status=" + status + '}'; } }
DTO对象
/** * UserFeedbackFormDto.java * * @author Zero */ public class UserFeedbackFormDto extends AbstractDto { private static final long serialVersionUID = -5003694127258885488L; //姓名 private String fullName; //职位 private String position; //电话 private String phone; //留言类型 private FeedbackType contentType; //留言内容 private String content; private String captcha; public UserFeedbackFormDto() { } public UserFeedbackFormDto(UserFeedback userfeedback) { super(userfeedback); this.fullName = userfeedback.fullName(); this.position = userfeedback.position(); this.phone = userfeedback.phone(); this.contentType = userfeedback.contentType(); this.content = userfeedback.content(); } public UserFeedbackFormDto(FeedbackType feedbackType) { this.contentType = feedbackType; } public FeedbackType[] getFeedbackTypes(){ return FeedbackType.values(); } //此处省略其他属性set/get方法 public static List<UserFeedbackFormDto> toDtos(List<UserFeedback> list) { List<UserFeedbackFormDto> dtos = new ArrayList<>(list.size()); dtos.addAll(list.stream().map(UserFeedbackFormDto::new).collect(Collectors.toList())); return dtos; } }
基于注解的控制器
Controller类
使用一个基于注解的控制器可以处理多个动作,利用Dispatcher Servlet实现请求转发,利用RESTful API实现基于URL的请求处理服务,通过调用封装了后端复杂逻辑的Servlet类实现业务逻辑处理。而且使用@RequestingMapping注解类型即可对一个方法进行请求处理,其请求映射不需要存储在配置文件中。控制器部分的代码如下:/** * UserFeedbackEmailController.java * 留言&&反馈 * * @author Zero */ //@Controller>>标识类是一个hander处理器 @Controller //@RequestingMapping>>注解控制器类时则所有的方法都将映射为类级别的请求,value属性将URL映射到方法 @RequestMapping("/contact/") public class UserFeedbackEmailController { //@Autowired>>service实例注入controller, @Autowired private UserFeedbackService userFeedbackService; //@RequestingMapping>>标识方法时则为一个请求处理方法,method属性指示该方法仅处理的http类型,返回值解析为跳转路径 @RequestMapping(value = "feedback_form", method = RequestMethod.POST) //@ModelAttribute>>前端请求参数绑定到formDto入参,formDto对象添加到model,返回类型为string时则为逻辑视图名 public String sendFeedbackEmail(@ModelAttribute("formDto") @Valid UserFeedbackFormDto formDto, BindingResult result, Model model, HttpServletRequest request) { //检查captcha final String correctCaptcha = WebUtils.getCaptchaKey(request.getSession()); final String captcha = formDto.getCaptcha(); if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(correctCaptcha)) { result.rejectValue("captcha", null, "验证码错误"); return "front/contact"; } ////@Valid>>validator验证器,判断bindingResult.hasErrors(),有误则返回dto对应Validator定制消息提示 if (result.hasErrors()) { return "front/contact"; } //service实现类调用sendFeedbackEmail方法,以实现具体的业务处理 String contactResult = userFeedbackService.sendFeedbackEmail(formDto); //创建model实例,传数据到前端 model.addAttribute("contactResult", contactResult); //redirect经过client,避免user重新加载页面时再调用相同action return "redirect:../contact"; } }
Validator类
控制器中使用了JSR 303 验证器,通过注解给对象属性添加约束以及定制错误消息。提交表单中的数据传输对象UserFeedbackFormDto对应的验证器代码如下:/** * UserFeedbackFormDtoValidator.java * 验证表单对象 * * @author Zero */ //@Component>>把普通pojo实例化到spring容器,这里把validator注入spring容器中 @Component //validator接口:validator编程实现数据验证的接口 public class UserFeedbackFormDtoValidator implements Validator { @Override //supports方法:判断当前验证器是否支持指定的clazz验证,如果支持返回true public boolean supports(Class<?> clazz) { return UserFeedbackFormDto.class.equals(clazz); } @Override //validate方法:验证目标对象target,将验证错误填入包含一系列FieldError和ObjectError对象的Errors对象 //Errors对象中的错误信息,可利用表单标签库的Errors标签显示在HTML页面,错误消息可通过Spring支持的国际化特性进行本地化 public void validate(Object target, Errors errors) { //给Errors对象添加错误的方法:在Errors对象上调用一个reject或rejectValue方法 //ValidationUtils是一个有助于编写Validator的工具类 ValidationUtils.rejectIfEmptyOrWhitespace(errors, "fullName", null, "姓名不能为空!"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "position", null, "职位不能为空!"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "contentType", null, "留言类型为选择!"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "content", null, "留言内容不能为空!"); UserFeedbackFormDto formDto = (UserFeedbackFormDto) target; validatePhoneNumber(formDto, errors); } private void validatePhoneNumber(UserFeedbackFormDto formDto, Errors errors) { final String phoneNumber = formDto.getPhone(); if (StringUtils.isEmpty(phoneNumber)) { errors.rejectValue("phone", null, "电话号码不能为空!"); return; } if (!MatchUtils.isPhoneNumber(phoneNumber)) { errors.rejectValue("phone", null, "电话号码格式错误!"); } } }
业务逻辑实现
Service接口与实现类
/** * UserFeedbackService.java * * @author Zero */ public interface UserFeedbackService { String sendFeedbackEmail(UserFeedbackFormDto formDto); }
/** * UserFeedbackServiceImpl.java * * @author Zero */ @Service("userFeedbackService") public class UserFeedbackServiceImpl implements UserFeedbackService { @Override public String sendFeedbackEmail(UserFeedbackFormDto formDto) { //创建handler对象,调用其hander方法处理具体业务 SendFeedbackEmailHandler handler = new SendFeedbackEmailHandler(formDto); return handler.handle(); } }
Handler业务处理类
/** * SendFeedbackEmailHandler.java * * @author Zero */ public class SendFeedbackEmailHandler { private static final Logger LOG = LoggerFactory.getLogger(SendFeedbackEmailHandler.class); private static final String SUCCESS_RESULT = "1"; private static final String SUBJECT = "有新的待处理留言信息"; //TEMPLATE为反馈邮件发送模板 private static final String TEMPLATE = "template/zh_feedback_root_notice.html"; private transient UserFeedbackRepository userFeedbackRepository = BeanProvider.getBean(UserFeedbackRepository.class); private transient MailRepository mailRepository = BeanProvider.getBean(MailRepository.class); private UserFeedbackFormDto formDto; public SendFeedbackEmailHandler(UserFeedbackFormDto formDto) { this.formDto = formDto; } public String handle() { //将留言写入数据库 UserFeedback userFeedback = newUserFeedback(); userFeedbackRepository.saveUserFeedback(userFeedback); //记录日志 AuditLog.create("添加留言(fullName = " + userFeedback.fullName() + ", uuid = " + userFeedback.uuid() + ")"); LOG.debug("{}|Save Feedback : {}", WebUtils.getIp(), userFeedback); //邮件反馈 sendNoticeEmailToRoot(userFeedback); return SUCCESS_RESULT; } private UserFeedback newUserFeedback() { return new UserFeedback() .fullName(formDto.getFullName()) .position(formDto.getPosition()) .phone(formDto.getPhone()) .contentType(formDto.getContentType()) .content(formDto.getContent()); } //发送邮件的具体实现 protected void sendNoticeEmailToRoot(UserFeedback userFeedback) { final IDPConfiguration idpConfiguration = IDPHolder.idpConfiguration(); //rootEmailAddress List<String> emails = idpConfiguration.rootEmailList(); if (null == emails || emails.size() < 1) { LOG.debug("{}| Not Found EmailAddress: {}", SecurityUtils.username(), emails); return; } Map<String, Object> params = new HashMap<>(); params.put("fullName", userFeedback.fullName()); params.put("position", userFeedback.position()); params.put("phone", userFeedback.phone()); params.put("contentType", userFeedback.contentType().getLabel()); params.put("content", userFeedback.content()); params.put("hostUrl", Holder.host() + "login"); STRender render = new STRender(TEMPLATE, params); String content = render.render(); for (String email : emails) { //MailTransmitter为邮件发送器 MailTransmitter mailTransmitter = new MailTransmitter().subject(SUBJECT).to(email).content(content); final MailTransmitResult result = mailRepository.sendMail(mailTransmitter); AuditLog.createMailLog(email, SUBJECT + "{发送结果:" + result + "}", "来自 " + userFeedback.fullName() + " 的" + userFeedback.contentType().getLabel() + "留言信息需要处理"); } } }
数据持久层
Repository接口
/** * UserFeedbackRepository.java * * @author Zero */ public interface UserFeedbackRepository { void saveUserFeedback(UserFeedback userfeedback); List<UserFeedback> findUserFeedbackList(Map<String,Object> map); long totalUserFeedbackList(Map<String,Object> map); }
Repository实现类
/** * UserFeedbackRepositoryMongoDB.java * * @author Zero */ @Repository("userFeedbackRepository") public class UserFeedbackRepositoryMongoDB extends AbstractMongoSupport implements UserFeedbackRepository { @Override public void saveUserFeedback(UserFeedback userfeedback) { this.mongoTemplate().save(userfeedback); } @Override public List<UserFeedback> findUserFeedbackList(Map<String, Object> map) { Query query = addPagination(new Query(),map); addFeedbackConditions(query,map); return this.mongoTemplate().find(query,UserFeedback.class); } private void addFeedbackConditions(Query query, Map<String, Object> map) { String enterpriseName = (String)map.get("enterpriseName"); String content = (String)map.get("content"); if(StringUtils.isNotEmpty(enterpriseName)){ addRegexCriteria(query,"enterpriseName",enterpriseName); } if(StringUtils.isNotEmpty(content)){ addRegexCriteria(query,"content",content); } query.with(new Sort(new Sort.Order[]{new Sort.Order(Sort.Direction.DESC, "createTime")})); } @Override public long totalUserFeedbackList(Map<String, Object> map) { Query query = addPagination(new Query(),map); addFeedbackConditions(query,map); return this.mongoTemplate().count(query, UserFeedback.class); } }
前端页面渲染
JSP页面表单
//contast.jsp <form:form commandName="formDto" action="${contextPath}/contact/feedback_form" cssClass="pubform ess-form"> <input type="hidden" name="forward" value="${contextPath}/contact"> <%--<tags:csrf/>--%> <div class="input-box"> <form:input path="fullName" placeholder="请输入姓名" required="true"/> <form:errors path="fullName" cssClass="label text-danger"/> </div> <div class="input-box"> <form:input path="position" placeholder="请输入职位" required="true"/> <form:errors path="position" cssClass="label text-danger"/> </div> <div class="input-box"> <form:input path="phone" placeholder="请输入手机号码" required="true"/> <span class="err errphone text-danger" style="display: block;"><form:errors path="phone"/></span> </div> <div class="input-box"> <form:select path="contentType" required="true"> <form:options items="${formDto.feedbackTypes}" itemLabel="label"/> </form:select> <form:errors path="contentType" cssClass="label text-danger"/> </div> <div class="input-box tex-box"> <form:textarea path="content" placeholder="请输入留言" required="true"/> <form:errors path="content" cssClass="label text-danger"/> </div> <div class="input-group input-box idencode-box"> <form:input path="captcha" onkeyup="value=value.replace(/[^\w\.\/]/ig,'')" name="captcha" class="form-control" maxlength="4" placeholder="请输入验证码" autocomplete="off" required="true"/> <span class="input-group-addon input-sm"> <img src="${pageContext.request.contextPath}/public/captcha.pngx" onclick="this.src = this.src+'?'" alt="Captcha" style="margin: -2px;"/> </span> <span class="err" style="display: block;"><form:errors path="captcha" cssClass="label text-danger"/></span> </div> <div class="input-box sub-box"><input type="submit" name="submit" id="submit" value="提交"/> </div> <c:if test="${param.contactResult eq '1'}"> <div class="success-box" id="connectSuccess"> <div class="bg"><span class="gou">✔</span></div> <div class="tit-success">提交成功</div> <p class="des-success">感谢您的关注。</p> <p class="tit-success" style="font-size: 16px"><span id="closeTime">3</span>秒后自动关闭</p> </div> </c:if> </form:form>
JS页面渲染
//contact.js var Contact = { init: function () { this.evBind(); }, evBind: function () { var self = this; self.connectSuccessHide(); $('#phone').on('blur',function(){ self.testTel($(this).val(),$('.errphone')) }) }, testTel: function (val, err) { var flag = false; if (!this.checkIdentity(val)) { err.html('请输入正确格式的手机号'); flag=true; } else { flag=false; err.html(''); } return flag }, checkIdentity: function (val) { return (/^1[34578]\d{9}$/.test(val)); }, connectSuccessHide:function(){ var time = 3; var closeTimer = setInterval(function(){ time--; if(time==0){ clearInterval(closeTimer); $('#connectSuccess').slideUp(400) } $('#closeTime').html(time); },1000) } } $(function(){ Contact.init(); })
相关文章推荐
- 利用第三方开源框架 SwipeMenuListView 实现用户的左右侧滑事件
- 在Spring MVC框架下利用Servlet3.0 API实现文件上传
- Android后台发送邮件实现用户反馈
- 【远程调用框架】如何实现一个简单的RPC框架(三)优化一:利用动态代理改变用户服务调用方式
- [置顶] 简析 React Native 用户反馈功能实现
- 利用邮件对象实现发送QQ日志以及检测用户是否开通SMTP功能
- 利用okhttp框架实现包含验证码的用户登录,保持session操作
- 利用okhttp框架实现包含验证码的用户登录,保持session操作(下)
- [置顶] spring mvc 利用maven实现不同环境使用不同配置文件
- 利用okhttp框架实现包含验证码的用户登录,保持session操作(上)
- [置顶] Win10下用IDEA搭建Struts2+Spring4+Hibernate5(SSH)框架,实现用户登录注册
- 使用maven与MyEclipse整合ssm(Spring MVC、Spring、Mybatis)三大框架并实现用户注册(环境搭载+实例源码下载)
- 【Spring学习笔记-MVC-5】利用spring MVC框架,实现ajax异步请求以及json数据的返回
- 利用rsync实现金笛邮件的用户数据的增量备份
- 利用ssh端口转发实现邮件转发
- 转帖:在MFC框架基础上实现系统托盘与用户的交互
- 在ASP.NET 中实现单点登录(利用Cache, 将用户信息保存在服务器缓存中)
- 利用Win2003中的NAT实现基于ADSL拨号上网(家庭用户)
- 利用HttpModuler实现WEB程序同一时间只让一个用户
- 利用HttpModuler实现WEB程序同一时间只让一个用户实例登陆