這篇文章是系列的一部分:
• Spring Security 註冊系列
• 使用 Spring Security 的註冊過程
• 註冊 – 通過電子郵件激活新帳户
• Spring Security 註冊 – 發送驗證電子郵件
• 使用 Spring Security 註冊 – 密碼編碼
• 註冊 API 變為 RESTful (當前文章)
• Spring Security – 重置密碼
• 註冊 – 密碼強度和規則
• 更新您的密碼
• 通知用户從新設備或位置登錄
1. 概述
在 Baeldung 上註冊系列文章中,我們已經以 MVC 方式構建了大部分所需的功能。
現在,我們將一些這些 API 轉換為更 RESTful 的方法。
2. The Register 操作
我們先來看主要的註冊操作:
@PostMapping("/user/registration")
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
- 我們現在返回一個 DTO 並直接將其映射到響應體中
- 我們不再處理方法中的錯誤處理
我們還移除了舊的 showRegistrationPage()
— 因為這對於僅僅顯示註冊頁面來説是不需要的。
3. The registration.html
此外,我們需要修改 registration.html 以:
- 使用 Ajax 提交註冊表單
- 接收操作結果作為 JSON
以下是:
<html>
<head>
<title th:text="#{label.form.title}">form</title>
</head>
<body>
<form method="POST" enctype="utf8">
<input name="firstName" value="" />
<span id="firstNameError" style="display:none"></span>
<input name="lastName" value="" />
<span id="lastNameError" style="display:none"></span>
<input name="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. 異常處理
通常,實施良好的異常處理策略可以使 REST 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);
}
接下來,我們還需要處理我們的自定義 Exception 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);
}
5. The GenericResponse
我們還需要改進 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對象包含全局錯誤
- 我們顯示每個字段錯誤在其字段旁邊
- 我們顯示所有全局錯誤在一個地方在表單的末尾
7. 結論
本文的重點是使API朝着更RESTful的方向發展,並展示一種簡單的方法來處理該API在前端。
jQuery前端本身並不是重點——它只是一個基本的潛在客户端,可以在多種JS框架中實現,而API保持不變。
下一條 »
Spring Security – 重置密碼
«
使用Spring Security進行註冊 – 密碼編碼
0 位用戶收藏了這個故事!