学习 Spring Security(七):RESTful 化注册 API

1、概述

在注册系列的最后几篇博文中,我们将以 MVC 方式来构建所需要到的大部分功能。

我们将把这些 API 中的一部分转换成更具 REST 风格。

2、注册操作

从注册操作开始:


@RequestMapping(value = "/user/registration", method = RequestMethod.POST)
@ResponseBody
public GenericResponse registerUserAccount(@Valid UserDto accountDto, HttpServletRequest request) {logger.debug("Registering user account with information: {}", accountDto);User registered = createUserAccount(accountDto);if (registered == null) {throw new UserAlreadyExistException();}String appUrl = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, request.getLocale(), appUrl));
 return new GenericResponse("success");
} 

这与原来的 MVC 实现有何不同呢?

  • 请求映射到 HTTP POST

  • 通过 @ResponseBody 注解直接返回一个合适的 DTO 并进行转换序列化,之后写入响应体

  • 不再处理方法中的错误处理

我们还删除了旧的 showRegistrationPage() —— 因为不需要显示注册页面。

3、registration.html

为了适应上述的变化,我们现在需要修改 registration.html

  • 使用 Ajax 提交注册表单

  • 以 JSON 的方式接收操作后响应的结果


<html>
<head>
<title th:text="#{label.form.title}">form</title>
</head>
<body>
<form action="/" method="POST" enctype="utf8"><inputname="firstName" value="" /><span id="firstNameError" style="display:none"></span><inputname="lastName" value="" /><span id="lastNameError" style="display:none"></span><inputname="email" value="" /> <span id="emailError" style="display:none"></span><input name="password" value="" type="password" /><span id="passwordError" style="display:none"></span><input name="matchingPassword" value="" type="password" /><span id="globalError" style="display:none"></span><a href="#" onclick="register()" th:text="#{label.form.submit}">submit</a>
</form><script src="jquery.min.js"></script>
<script type="text/javascript"> var serverContext = [[@{/}]];
 
function register(){$(".alert").html("").hide();var formData= $('form').serialize();$.post(serverContext + "/user/registration",formData ,function(data){if(data.message == "success"){window.location.href = serverContext +"/successRegister.html";}}).fail(function(data) {if(data.responseJSON.error.indexOf("MailError") > -1){window.location.href = serverContext + "/emailError.html";}else if(data.responseJSON.error.indexOf("InternalError") > -1){window.location.href = serverContext + "/login.html?message=" + data.responseJSON.message;}else if(data.responseJSON.error == "UserAlreadyExist"){$("#emailError").show().html(data.responseJSON.message);}else{var errors = $.parseJSON(data.responseJSON.message);$.each( errors, function( index,item ){$("#"+item.field+"Error").show().html(item.defaultMessage);});errors = $.parseJSON(data.responseJSON.error);$.each( errors, function( index,item ){$("#globalError").show().append(item.defaultMessage+"<br>");});}});
} </script>
</body>
</html> 

4、异常处理

随着 RESTful API 数量的增长,异常处理逻辑当然也会变得更加成熟。

我们使用 @ControllerAdvice 机制来干净利落地处理程序抛出的异常,不过 我们需要一种新的异常类型。

BindException —— 在 UserDto 验证(如果无效)时抛出。我们将重写默认的 ResponseEntityExceptionHandler 中的 handleBindException() 方法,往响应体中添加错误信息:


@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {logger.error("400 Status Code", ex);BindingResult result = ex.getBindingResult();GenericResponse bodyOfResponse = new GenericResponse(result.getFieldErrors(), result.getGlobalErrors()); return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
} 

我们还需要处理自定义异常 UserAlreadyExistException —— 当用户注册一个已经存在的电子邮件时就会抛出该异常:


@ExceptionHandler({ UserAlreadyExistException.class })
public ResponseEntity<Object> handleUserAlreadyExist(RuntimeException ex, WebRequest request) {logger.error("409 Status Code", ex);GenericResponse bodyOfResponse = new GenericResponse(messages.getMessage("message.regError", null, request.getLocale()), "UserAlreadyExist"); return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
} 

6、通用响应

我们还需要改进 GenericResponse 实现来保存这些验证错误信息:


public class GenericResponse {
 public GenericResponse(List<FieldError> fieldErrors, List<ObjectError> globalErrors) {super();ObjectMapper mapper = new ObjectMapper();try {this.message = mapper.writeValueAsString(fieldErrors);this.error = mapper.writeValueAsString(globalErrors);} catch (JsonProcessingException e) {this.message = "";this.error = "";}}
} 

6、UI —— 字段和全局错误

最后,让我们看看如何使用 jQuery 处理字段和全局错误:


var serverContext = [[@{/}]];
 
function register(){$(".alert").html("").hide();var formData= $('form').serialize();$.post(serverContext + "/user/registration",formData ,function(data){if(data.message == "success"){window.location.href = serverContext +"/successRegister.html";}}).fail(function(data) {if(data.responseJSON.error.indexOf("MailError") > -1){window.location.href = serverContext + "/emailError.html";}else if(data.responseJSON.error.indexOf("InternalError") > -1){window.location.href = serverContext + "/login.html?message=" + data.responseJSON.message;}else if(data.responseJSON.error == "UserAlreadyExist"){$("#emailError").show().html(data.responseJSON.message);}else{var errors = $.parseJSON(data.responseJSON.message);$.each( errors, function( index,item ){$("#"+item.field+"Error").show().html(item.defaultMessage);});errors = $.parseJSON(data.responseJSON.error);$.each( errors, function( index,item ){$("#globalError").show().append(item.defaultMessage+"<br>");});}});
} 

注意:

  • 如果存在验证错误,则 message 对象包含 error 字段,error 对象包含全局错误

  • 我们在每个字段旁边显示该字段的错误信息

  • 我们在表单的最后一个地方显示所有的全局错误

7、结论

这篇博文的重点是将 API 转换 RESTful 风格,并简单介绍了前端处理 API 的方法。

jQuery 前端在本次内容中并不是重点,它只是一个基础的客户端类库,您可以在任何 JS 框架中实现这些逻辑,而后端的 API 仍然完全相同。

本教程的完整实现可以在 github 项目中找到。

原文项目地址

github.com/eugenp/spri…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值