在我們日常的工作中,引數校驗是一項非常重要的任務。它能夠幫助我們確保程式碼的穩定性和可靠性,避免因為引數錯誤而導致的意外情況發生。引數校驗通常包括對輸入引數的合法性、格式的檢查等。而為了提高程式碼的可維護性和可重用性,我們可以使用一些常見的引數校驗方法。
另外,國際化也是一項非常重要的功能,尤其是在跨國專案中。通過國際化的設定,我們可以根據使用者的語言環境,自動切換顯示的語言,提供更好的使用者體驗。在引數校驗中,我們也可以使用國際化設定來返回相應的提示資訊,使得提示資訊能夠適應不同的語言環境。
好的,讓我們一步一步來介紹引數校驗的方法。除了上述提到的方法,如果還有一些其他的優秀方法可以用來進行引數校驗,也可以提醒我一下!
RequestParam本身就有對引數進行的基礎校驗,比如required表示是否引數必填。可以通過在方法引數上使用@RequestParam註解來實現引數校驗。以下是範例程式碼:
@GetMapping("/hello-not-required")
public String sayHelloByNotRequired(@RequestParam(required = false, name = "who") String who) {
if (StrUtil.isBlank(who)) {
who = "World";
}
return StrUtil.format("Hello, {}!", who);
}
如果不傳遞引數,也不會有任何問題,這裡不做演示就可以了,非常簡單。
然而,雖然RequestParam只能指示引數是否被攜帶,但它並不負責檢驗引數值是否為空。因此,我們可以通過與NotEmpty註解結合使用來進行判斷。
@GetMapping("/hello-required")
public String sayHelloByRequired(@RequestParam @NotEmpty String who) {
return StrUtil.format("Hello, {}!", who);
}
當存取http://localhost:8080/demo/hello-required?who=
時,我們的程式是報錯的,因為who欄位不能為空
如果你仔細閱讀了NotEmpty註解的檔案,你會發現它允許自定義報錯提示資訊。因此,我們可以嘗試自定義提示資訊,以滿足我們的需求。
@GetMapping("/hello-required-message")
public String sayHelloByRequiredWithMessage(@RequestParam @NotEmpty(message = "who must be not null") String who) {
return StrUtil.format("Hello, {}!", who);
}
我再來為你演示一下,除了NotEmpty註解,還有許多其他已經內建的註解可供使用。我就不一一列舉了,你可以自行探索更多的註解。
除了上面提到的簡單校驗方式,我們在實際開發中更常使用的是通過JSON傳遞引數,並使用實體類接收引數。下面是相關程式碼範例:
@PostMapping("/hello-required-message-object")
public String sayHelloByRequiredWithMessageForObject(@RequestBody @Valid MyTest test) {
return StrUtil.format("Hello, {}!", test);
}
public class MyTest {
@NotEmpty(message = "NotEmpty.message")
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
加長優化語句:為了方便演示,我特意更換了一些提示資訊,以便更清晰地展示給大家。接下來,我們再仔細觀察一下。
其實在工作中,校驗引數是一個常見的任務。我們通常會使用程式碼進行各種校驗,因為我們對引數的要求遠遠超出了簡單的非空檢查。除了非空檢查,我們還需要進行其他等值校驗等。因此,如果在程式碼中進行校驗,我們可以選擇丟擲異常或直接返回錯誤資訊。舉個例子,我可以使用自定義異常來說明這一點:
@PostMapping("/hello-required-message-i18n")
public String sayHelloByRequiredWithMessageFori18n() {
throw new CustomException("notBlank.message");
}
程式碼經過優化,已經去除了多餘的邏輯,直接通過丟擲異常來處理。讓我來演示一下。
雖然在出錯時會有報錯資訊返回,但是這些錯誤資訊並不完全符合業務返回資料的格式。為了解決這個問題,我們需要定義一個全域性例外處理類來統一處理異常情況。
@RestControllerAdvice
public class GlobalExceptionHandler {
@Autowired
private MessageSource messageSource;
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleCustomException(CustomException ex, Locale locale) {
String errorMessage = messageSource.getMessage(ex.getMessage(), null, locale);
ErrorResponse errorResponse = new ErrorResponse(500, errorMessage);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
ErrorResponse
是我單獨定義的錯誤描述類,我就不寫了,因為每個專案都會有自己的返回類。
讓我們進一步探討一下關於國際化資訊的客製化。首先,為了實現這一目標,你需要在應用程式中進行相關的設定,具體來說,就是在application.yaml檔案中進行必要的設定。
server:
port: 8080
servlet:
context-path: /demo
spring:
messages:
baseName: i18n/messages
encoding: UTF-8
mvc:
locale: zh_CN
請注意,baseName在這裡表示路徑,而最後的messages並不代表包的含義,而是檔案的字首。如果無法找到messages_zh_CN檔案,則會嘗試查詢messages_zh檔案,如果還找不到,則會查詢messages檔案。因此,請不要再建立一個名為messages的包。
當所有準備工作完成後,我們可以檢查演示結果是否已經改變了欄位資訊。
現在基本上已經存在了全域性例外處理機制,不過我們還需要進一步優化。現在讓我們來討論一下自定義註解的使用。首先,我們注意到錯誤資訊無法給出具體的欄位值。顯然,我們不能每次都手動寫上每個欄位的名稱並提示不能為空。為了解決這個問題,我們需要自定義一個註解來實現自動化的校驗。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmptyField {
String message() default "";
}
有了註解後,下一步就是通過切面來獲取並處理這個註解。接下來,我們來編寫相應的程式碼來實現這個功能:
@Aspect
@Component
public class NotEmptyFieldAspect {
@Autowired
private MessageSource messageSource;
@Around("within(com.xkcoding..*)")
public Object validateNotEmptyField(ProceedingJoinPoint joinPoint) throws Throwable {
// 獲取目標方法的參數列
Object[] args = joinPoint.getArgs();
// 遍歷參數列
for (Object arg : args) {
// 獲取引數實體類的屬性
Field[] fields = arg.getClass().getDeclaredFields();
// 遍歷屬性列表
for (Field field : fields) {
// 判斷屬性是否被 @NotEmptyField 註解修飾
if (field.isAnnotationPresent(NotEmptyField.class)) {
// 獲取註解資訊
NotEmptyField annotation = field.getAnnotation(NotEmptyField.class);
field.setAccessible(true);
// TODO: 進一步處理邏輯
if (ObjectUtil.isEmpty(field.get(arg))) {
String message = messageSource.getMessage(annotation.message(), null, Locale.getDefault());
throw new CustomException(field.getName() + message);
}
// 輸出屬性和註解資訊
System.out.println("屬性:" + field.getName());
System.out.println("註解資訊:" + annotation.message());
System.out.println("值:" + field.get(arg));
}
}
}
// 呼叫目標方法
Object result = joinPoint.proceed();
return result;
}
}
我只是簡單地寫了一下實現邏輯,並沒有進行優化操作,所以以上程式碼僅供參考。現在讓我們來看一下外層程式碼:
@PostMapping("/hello-required-message-i18n-object")
public String sayHelloByRequiredWithMessageFori18nObject(@RequestBody @Valid MyTestForAspect myTestForAspect) {
return "";
}
當我們完成所有的準備工作之後,讓我們來觀察一下演示情況:
在我們的工作中,引數校驗是一項不可或缺的重要任務。因此,本文只是初步探討了可以進行優化的方面,而並未詳細闡述如何完美地進行優化。如果我遺漏了一些解決方案,也歡迎大家提供寶貴的建議和提醒。我的目的只是提供一些思路和引發討論,以期能夠共同進步。