您的位置:首页 > 数据库 > Mongodb

[置顶] 在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数据库后,即可进行该功能的实现。

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();
})
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐