精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

看看人家,那后端API接口寫得,那叫一個(gè)優(yōu)雅!

開發(fā) 前端
寫優(yōu)雅的 API 就像做一道好菜,食材(技術(shù))重要,火候(經(jīng)驗(yàn))重要,用心(態(tài)度)更重要。一個(gè)優(yōu)雅的 API 能體現(xiàn)開發(fā)者的專業(yè)素養(yǎng),也能讓整個(gè)團(tuán)隊(duì)的協(xié)作效率事半功倍。

兄弟們,作為一名 Java 后端開發(fā)者,我敢打賭你一定遇到過(guò)這種情況:對(duì)接別人的 API 時(shí)順風(fēng)順?biāo)?,代碼寫得行云流水,下班前就能輕松搞定;而對(duì)接某些 API 時(shí),文檔看得一頭霧水,參數(shù)傳得懷疑人生,調(diào)試到深夜還在群里卑微提問(wèn):"大佬,這個(gè)接口到底要傳啥?。?

這就是優(yōu)雅與粗糙的 API 之間的天壤之別。一個(gè)優(yōu)雅的 API 就像一位貼心的服務(wù)員,你剛抬手就知道要加水;而一個(gè)糟糕的 API 就像走進(jìn)了迷宮餐廳,找個(gè)座位都得求助三次。今天咱們就來(lái)好好聊聊,如何把自己的后端 API 接口打造成 "五星級(jí)服務(wù)水準(zhǔn)",讓同事和調(diào)用者都忍不住點(diǎn)贊。

一、命名規(guī)范:給你的 API 起個(gè) "好記的名字"

首先咱們得明確一點(diǎn):API 是給人用的,不是給機(jī)器看的。機(jī)器只認(rèn) 0 和 1,但人需要清晰的語(yǔ)義。想象一下,你去飯店吃飯,菜單上寫著 "固體碳水化合物配高溫油炸蛋白質(zhì)",你知道這是啥嗎?其實(shí)就是 "炸薯?xiàng)l配炸雞" 啊!

資源命名的黃金法則

優(yōu)雅的 API 命名首先要遵循 "資源導(dǎo)向" 原則。什么意思?就是用名詞而不是動(dòng)詞來(lái)表示 API 操作的資源。比如:

// 反面教材:用動(dòng)詞做資源名
@GetMapping("/getUserById")
@GetMapping("/deleteUser")
@GetMapping("/updateUserInfo")
// 優(yōu)雅示范:用名詞表示資源
@GetMapping("/users/{id}")    // 獲取用戶
@DeleteMapping("/users/{id}") // 刪除用戶
@PutMapping("/users/{id}")    // 更新用戶

看到?jīng)]?后者就像菜單上的 "宮保雞丁",一看就知道是啥;前者則像 "廚師現(xiàn)做雞肉花生米炒辣椒",啰嗦又難懂。RESTful 風(fēng)格之所以流行,就是因?yàn)樗?HTTP 方法(GET/POST/PUT/DELETE)來(lái)表示操作,用 URL 來(lái)表示資源,天然符合人類的理解習(xí)慣。

復(fù)數(shù)還是單數(shù)?這是個(gè)問(wèn)題

關(guān)于資源名用復(fù)數(shù)還是單數(shù),業(yè)界一直有爭(zhēng)論。我的建議是:用復(fù)數(shù)。因?yàn)?API 操作的通常是資源集合中的一個(gè)或多個(gè)元素,比如/users表示用戶集合,/users/123表示集合中的某個(gè)用戶。就像你去水果店買蘋果,老板會(huì)問(wèn)你 "要幾個(gè)蘋果" 而不是 "要幾個(gè)蘋果項(xiàng)"。

// 推薦寫法
@GetMapping("/users")       // 獲取用戶列表
@GetMapping("/users/{id}")  // 獲取單個(gè)用戶
// 不推薦寫法
@GetMapping("/user")        // 單個(gè)用戶?還是所有用戶?
@GetMapping("/userList")    // 冗余,復(fù)數(shù)已經(jīng)表示列表

版本控制要直白

API 迭代是難免的,這時(shí)候版本控制就很重要了。我見(jiàn)過(guò)最離譜的版本控制是用日期做版本號(hào),比如/api/20250818/users,每次更新都得改日期,調(diào)用者苦不堪言。

優(yōu)雅的做法是在 URL 中明確標(biāo)注主版本號(hào):

// 推薦方式
@GetMapping("/api/v1/users")  // v1表示第一版API
@GetMapping("/api/v2/users")  // v2表示不兼容的升級(jí)版
// 不推薦方式
@GetMapping("/api/users?version=1")  // 版本藏在參數(shù)里
@GetMapping("/newApi/users")         // 用新老區(qū)分,后續(xù)版本怎么辦?

這樣做的好處是一目了然,調(diào)用者可以清晰知道自己在用哪個(gè)版本的 API,升級(jí)時(shí)也能有明確的遷移目標(biāo)。

二、統(tǒng)一響應(yīng):給調(diào)用者一個(gè) "定心丸"

想象一下這種場(chǎng)景:調(diào)用 API 時(shí),成功返回的是{code:200, data:{...}},失敗返回的是{status:false, message:"出錯(cuò)了"},偶爾還會(huì)直接返回異常堆棧信息。這種 "薛定諤的響應(yīng)格式" 會(huì)讓前端同學(xué)崩潰的!

優(yōu)雅的 API 必須有統(tǒng)一的響應(yīng)格式,就像飯店不管上什么菜,都會(huì)用符合餐廳風(fēng)格的盤子裝一樣。

響應(yīng)體的標(biāo)準(zhǔn)結(jié)構(gòu)

一個(gè)標(biāo)準(zhǔn)的 API 響應(yīng)體應(yīng)該包含這些要素:

  • 狀態(tài)碼(code):業(yè)務(wù)層面的狀態(tài)標(biāo)識(shí)
  • 消息(message):操作結(jié)果的文字描述
  • 數(shù)據(jù)(data):實(shí)際返回的業(yè)務(wù)數(shù)據(jù)
  • 時(shí)間戳(timestamp):響應(yīng)時(shí)間,方便排查問(wèn)題

我們可以定義一個(gè)通用的響應(yīng) DTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
    // 業(yè)務(wù)狀態(tài)碼,200表示成功
    private int code;
    // 響應(yīng)消息
    private String message;
    // 響應(yīng)數(shù)據(jù)
    private T data;
    // 響應(yīng)時(shí)間戳
    private long timestamp;
    // 成功響應(yīng)的靜態(tài)方法
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "操作成功", data, System.currentTimeMillis());
    }
    // 錯(cuò)誤響應(yīng)的靜態(tài)方法
    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null, System.currentTimeMillis());
    }
}

使用這種統(tǒng)一響應(yīng)后,所有接口的返回格式保持一致:

@GetMapping("/users/{id}")
public ApiResponse<UserDTO> getUser(@PathVariable Long id) {
    UserDTO user = userService.findById(id);
    return ApiResponse.success(user);
}
// 響應(yīng)結(jié)果:
{
  "code": 200,
  "message": "操作成功",
  "data": {
    "id": 123,
    "name": "張三"
  },
  "timestamp": 1629260852341
}

異常處理:不能讓調(diào)用者猜謎

當(dāng) API 發(fā)生錯(cuò)誤時(shí),最忌諱的就是返回模棱兩可的錯(cuò)誤信息。優(yōu)雅的 API 應(yīng)該像醫(yī)生看病一樣,清晰告訴調(diào)用者 "哪里出了問(wèn)題"、"怎么解決"。

在 Spring Boot 中,我們可以用全局異常處理器來(lái)統(tǒng)一處理所有異常:

@RestControllerAdvice
public class GlobalExceptionHandler {
    // 處理業(yè)務(wù)異常
    @ExceptionHandler(ApplicationException.class)
    public ApiResponse<Void> handleApplicationException(ApplicationException e) {
        log.error("業(yè)務(wù)異常: {}", e.getMessage());
        return ApiResponse.error(e.getCode(), e.getMessage());
    }
    // 處理參數(shù)校驗(yàn)異常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResponse<Void> handleValidationException(MethodArgumentNotValidException e) {
        // 獲取校驗(yàn)失敗的字段和消息
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(error -> error.getField() + ":" + error.getDefaultMessage())
                .collect(Collectors.joining("; "));
        log.error("參數(shù)校驗(yàn)異常: {}", message);
        return ApiResponse.error(400, "參數(shù)錯(cuò)誤: " + message);
    }
    // 處理未知異常
    @ExceptionHandler(Exception.class)
    public ApiResponse<Void> handleUnknownException(Exception e) {
        log.error("未知異常", e); // 記錄完整堆棧
        // 給用戶返回友好信息,不暴露敏感內(nèi)容
        return ApiResponse.error(500, "服務(wù)器內(nèi)部錯(cuò)誤,請(qǐng)聯(lián)系管理員");
    }
}

然后定義業(yè)務(wù)異常類,讓異常能夠 "自我描述":

@Data
@AllArgsConstructor
public class ApplicationException extends RuntimeException {
    private int code;
    private String message;
    // 靜態(tài)方法簡(jiǎn)化異常創(chuàng)建
    public static ApplicationException of(int code, String message) {
        return new ApplicationException(code, message);
    }
    // 常用異常枚舉
    public static ApplicationException resourceNotFound() {
        return new ApplicationException(404, "資源不存在");
    }
    
    public static ApplicationException permissionDenied() {
        return new ApplicationException(403, "沒(méi)有操作權(quán)限");
    }
}

這樣處理后,當(dāng)業(yè)務(wù)發(fā)生異常時(shí),我們只需在代碼中拋出異常:

public UserDTO findById(Long id) {
    User user = userMapper.selectById(id);
    if (user == null) {
        throw ApplicationException.resourceNotFound();
    }
    return convertToDTO(user);
}

調(diào)用者會(huì)收到清晰的錯(cuò)誤提示,而不是一堆晦澀的異常堆棧。這種方式既保證了代碼的整潔(不用在 Controller 層寫大量 try-catch),又能給調(diào)用者明確的反饋,可謂一舉兩得。

三、參數(shù)校驗(yàn):把問(wèn)題擋在門外

你有沒(méi)有遇到過(guò)這種情況:接口實(shí)現(xiàn)里充滿了各種參數(shù)校驗(yàn)邏輯,一半代碼都在做 "如果參數(shù) A 為空怎么辦"、"如果參數(shù) B 格式不對(duì)怎么辦"。這種代碼不僅臃腫,而且容易遺漏校驗(yàn)邏輯。

優(yōu)雅的 API 應(yīng)該在入口處就做好參數(shù)校驗(yàn),就像火車站的安檢一樣,危險(xiǎn)品根本進(jìn)不了站。

JSR-303 校驗(yàn)的正確姿勢(shì)

Spring Boot 支持 JSR-303 規(guī)范的參數(shù)校驗(yàn),通過(guò)注解就能輕松實(shí)現(xiàn)參數(shù)校驗(yàn):

@Data
public class UserCreateDTO {
    @NotBlank(message = "用戶名不能為空")
    @Size(min = 2, max = 20, message = "用戶名長(zhǎng)度必須在2-20之間")
    private String username;

    @NotBlank(message = "密碼不能為空")
    @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[@#$%^&*])[a-zA-Z0-9@#$%^&*]{8,20}$", 
             message = "密碼必須包含字母、數(shù)字和特殊字符,長(zhǎng)度8-20")
    private String password;

    @NotNull(message = "年齡不能為空")
    @Min(value = 0, message = "年齡不能小于0")
    @Max(value = 150, message = "年齡不能大于150")
    private Integer age;

    @Email(message = "郵箱格式不正確")
    private String email;
}

在 Controller 中只需添加@Valid注解啟用校驗(yàn):

@PostMapping("/users")
public ApiResponse<UserDTO> createUser(@Valid @RequestBody UserCreateDTO userDTO) {
    // 校驗(yàn)通過(guò)才會(huì)執(zhí)行到這里
    UserDTO created = userService.create(userDTO);
    return ApiResponse.success(created);
}

當(dāng)參數(shù)不符合要求時(shí),會(huì)自動(dòng)拋出MethodArgumentNotValidException,然后被我們前面定義的全局異常處理器捕獲,返回統(tǒng)一的錯(cuò)誤響應(yīng):

{
  "code": 400,
  "message": "參數(shù)錯(cuò)誤: username:用戶名不能為空; password:密碼格式不正確",
  "data": null,
  "timestamp": 1629261523456
}

這種方式把校驗(yàn)邏輯和業(yè)務(wù)邏輯完美分離,代碼瞬間清爽了不少。

分組校驗(yàn):應(yīng)對(duì)復(fù)雜場(chǎng)景

有時(shí)候同一個(gè) DTO 在不同場(chǎng)景下有不同的校驗(yàn)規(guī)則。比如創(chuàng)建用戶時(shí)不需要傳 ID(自動(dòng)生成),但更新用戶時(shí)必須傳 ID。這時(shí)候可以用分組校驗(yàn)來(lái)解決:

// 定義分組接口
publicinterfaceCreateGroup {}
publicinterfaceUpdateGroup {}

@Data
public class UserDTO {
    // 更新時(shí)必須傳ID,創(chuàng)建時(shí)不需要
    @NotNull(message = "ID不能為空", groups = UpdateGroup.class)
    private Long id;

    @NotBlank(message = "用戶名不能為空", groups = {CreateGroup.class, UpdateGroup.class})
    private String username;

    // 創(chuàng)建時(shí)必須傳密碼,更新時(shí)可以不傳
    @NotBlank(message = "密碼不能為空", groups = CreateGroup.class)
    private String password;
}

// 在Controller中指定分組
@PostMapping("/users")
public ApiResponse<UserDTO> createUser(@Validated(CreateGroup.class) @RequestBody UserDTO userDTO) {
    // 創(chuàng)建用戶邏輯
}

@PutMapping("/users")
public ApiResponse<UserDTO> updateUser(@Validated(UpdateGroup.class) @RequestBody UserDTO userDTO) {
    // 更新用戶邏輯
}

通過(guò)分組校驗(yàn),我們可以用一個(gè) DTO 應(yīng)對(duì)不同場(chǎng)景,避免創(chuàng)建大量類似的 DTO 類。

四、文檔自動(dòng)生成:別讓文檔成為負(fù)擔(dān)

"寫 API 文檔" 大概是很多開發(fā)者最頭疼的事情之一。要么是寫完代碼忘了更新文檔,導(dǎo)致文檔和實(shí)際接口不一致;要么是文檔寫得太簡(jiǎn)略,調(diào)用者看得云里霧里。

優(yōu)雅的 API 應(yīng)該有清晰的文檔,而且最好是自動(dòng)生成的,免得 "寫文檔" 成為額外負(fù)擔(dān)。

OpenAPI 3.1 + SpringDoc:文檔自動(dòng)生成方案

OpenAPI 3.1(原 Swagger)是目前最流行的 API 文檔規(guī)范,它可以根據(jù)代碼自動(dòng)生成交互式 API 文檔。SpringDoc 是 Spring Boot 的 OpenAPI 實(shí)現(xiàn),用法非常簡(jiǎn)單。

首先添加依賴:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.1.0</version>
</dependency>

然后添加配置:

@Configuration
@OpenAPIDefinition(
    info = @Info(
        title = "用戶管理API",
        version = "v1",
        description = "用戶CRUD及相關(guān)操作的API接口文檔",
        contact = @Contact(name = "技術(shù)支持", email = "support@example.com")
    ),
    servers = {
        @Server(url = "http://localhost:8080", description = "開發(fā)環(huán)境"),
        @Server(url = "http://api.example.com", description = "生產(chǎn)環(huán)境")
    }
)
public class OpenApiConfig {
    // 可以配置安全方案,如JWT認(rèn)證
    @Bean
    public SecurityScheme securityScheme() {
        returnSecurityScheme.builder()
                .type(SecurityScheme.Type.HTTP)
                .scheme("bearer")
                .bearerFormat("JWT")
                .name("Authorization")
                .build();
    }
}

接下來(lái)在接口和 DTO 中添加注釋:

@Operation(summary = "創(chuàng)建用戶", description = "新增用戶信息,返回創(chuàng)建成功的用戶詳情")
@ApiResponses({
    @ApiResponse(responseCode = "200", description = "創(chuàng)建成功"),
    @ApiResponse(responseCode = "400", description = "參數(shù)錯(cuò)誤")
})
@PostMapping("/users")
public ApiResponse<UserDTO> createUser(
    @Parameter(description = "用戶信息", required = true)
    @Valid @RequestBody UserCreateDTO userDTO) {
    // 業(yè)務(wù)邏輯
}

啟動(dòng)項(xiàng)目后訪問(wèn)http://localhost:8080/swagger-ui.html,就能看到交互式的 API 文檔了。調(diào)用者可以直接在頁(yè)面上測(cè)試接口,查看參數(shù)說(shuō)明,再也不用對(duì)著文檔猜參數(shù)了。OpenAPI 3.1 相比之前的版本最大的改進(jìn)是完全兼容 JSON Schema,這意味著你的 JSON 數(shù)據(jù)結(jié)構(gòu)定義可以在 API 文檔和代碼校驗(yàn)中共享,避免重復(fù)定義。

五、安全設(shè)計(jì):API 也需要 "防盜門"

優(yōu)雅的 API 不僅要好用,更要安全。想象一下,你辛辛苦苦設(shè)計(jì)的 API,如果沒(méi)有安全防護(hù),就像家里沒(méi)裝防盜門,很容易被 "不速之客" 光顧。

Token 認(rèn)證:給 API 加把鎖

最常見(jiàn)的 API 安全方案是 Token 認(rèn)證,流程如下:

  1. 客戶端使用用戶名密碼換取 Token
  2. 客戶端每次請(qǐng)求 API 時(shí)在 Header 中攜帶 Token
  3. 服務(wù)器驗(yàn)證 Token 的有效性,通過(guò)則允許訪問(wèn)

實(shí)現(xiàn) JWT(JSON Web Token)認(rèn)證的代碼示例:

@RestController
@RequestMapping("/auth")
publicclass AuthController {
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/login")
    public ApiResponse<String> login(@RequestBody LoginRequest request) {
        // 驗(yàn)證用戶名密碼
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
        );
        
        // 生成Token
        String token = tokenProvider.generateToken(authentication);
        return ApiResponse.success(token);
    }
}

// JWT工具類
@Component
publicclass JwtTokenProvider {
    @Value("${app.jwt.secret}")
    privateString jwtSecret;
    
    @Value("${app.jwt.expiration}")
    private long jwtExpirationMs;

    // 生成Token
    publicString generateToken(Authentication authentication) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Date now = newDate();
        Date expiryDate = newDate(now.getTime() + jwtExpirationMs);
        
        return Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }
    
    // 驗(yàn)證Token
    publicboolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
            returntrue;
        } catch (Exception e) {
            // Token無(wú)效或過(guò)期
            returnfalse;
        }
    }
}

// 認(rèn)證過(guò)濾器
@Component
publicclass JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protectedvoid doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) {
        // 從Header中獲取Token
        String token = extractTokenFromHeader(request);
        
        // 驗(yàn)證Token
        if (token != null && tokenProvider.validateToken(token)) {
            // 設(shè)置認(rèn)證信息
            Authentication auth = getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        
        filterChain.doFilter(request, response);
    }
}

接口限流:防止 "暴飲暴食"

即使加了認(rèn)證,我們的 API 還需要防止被頻繁調(diào)用導(dǎo)致服務(wù)崩潰。就像飯店會(huì)限制同一桌的點(diǎn)餐數(shù)量,API 也需要限流保護(hù)。

使用 Spring Cloud Gateway 或 Sentinel 可以實(shí)現(xiàn)限流,簡(jiǎn)單場(chǎng)景下也可以用 Redis 自己實(shí)現(xiàn):

@Component
publicclass RateLimiter {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 嘗試獲取令牌,返回true表示允許訪問(wèn)
    public boolean tryAcquire(String key, int maxRequests, int periodSeconds) {
        String redisKey = "rate_limit:" + key;
        // 使用Redis的INCR命令自增
        Long count = redisTemplate.opsForValue().increment(redisKey);
        
        // 第一次訪問(wèn),設(shè)置過(guò)期時(shí)間
        if (count != null && count == 1) {
            redisTemplate.expire(redisKey, periodSeconds, TimeUnit.SECONDS);
        }
        
        // 判斷是否超過(guò)限制
        return count != null && count <= maxRequests;
    }
}

// 在Controller中使用
@GetMapping("/users")
public ApiResponse<List<UserDTO>> getUsers() {
    String clientIp = getClientIp(request);
    // 限制同一IP每分鐘最多訪問(wèn)60次
    if (!rateLimiter.tryAcquire(clientIp, 60, 60)) {
        throw ApplicationException.of(429, "請(qǐng)求過(guò)于頻繁,請(qǐng)稍后再試");
    }
    // 業(yè)務(wù)邏輯
}

敏感數(shù)據(jù)脫敏:保護(hù)用戶隱私

API 返回的響應(yīng)中可能包含敏感信息,如手機(jī)號(hào)、郵箱、身份證號(hào)等。優(yōu)雅的 API 應(yīng)該對(duì)這些信息進(jìn)行脫敏處理:

public class SensitiveDataUtils {
    // 手機(jī)號(hào)脫敏:138****5678
    publicstaticString maskPhone(String phone) {
        if (phone == null || phone.length() != 11) {
            return phone;
        }
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }
    
    // 郵箱脫敏:z***@example.com
    publicstaticString maskEmail(String email) {
        if (email == null || !email.contains("@")) {
            return email;
        }
        String[] parts = email.split("@");
        if (parts[0].length() <= 1) {
            return parts[0] + "***@" + parts[1];
        }
        return parts[0].substring(0, 1) + "***@" + parts[1];
    }
}

// 在DTO中使用
@Data
publicclass UserDTO {
    private Long id;
    privateString username;
    
    // 序列化時(shí)自動(dòng)脫敏
    @JsonSerialize(using = SensitivePhoneSerializer.class)
    privateString phone;
    
    @JsonSerialize(using = SensitiveEmailSerializer.class)
    privateString email;
}

六、性能優(yōu)化:讓 API 跑起來(lái)

優(yōu)雅的 API 不僅要好用、安全,還得跑得快。沒(méi)人愿意用一個(gè)響應(yīng)慢吞吞的 API,就像沒(méi)人愿意等上半小時(shí)才上菜的餐廳。

緩存策略:減少重復(fù)勞動(dòng)

對(duì)于頻繁訪問(wèn)且不常變化的數(shù)據(jù),緩存是提升性能的利器。我們可以用 Spring 的緩存抽象來(lái)實(shí)現(xiàn):

@Configuration
@EnableCaching
publicclass CacheConfig {
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 配置Redis緩存
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10)) // 默認(rèn)過(guò)期時(shí)間
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        // 針對(duì)不同緩存設(shè)置不同過(guò)期時(shí)間
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("users", config.entryTtl(Duration.ofHours(1)));
        cacheConfigurations.put("roles", config.entryTtl(Duration.ofHours(24)));
        
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }
}

// 在Service中使用緩存
@Service
publicclass UserService {
    // 查詢時(shí)先查緩存,沒(méi)有再查數(shù)據(jù)庫(kù)
    @Cacheable(value = "users", key = "#id")
    public UserDTO findById(Long id) {
        // 數(shù)據(jù)庫(kù)查詢邏輯
    }
    
    // 更新時(shí)同步更新緩存
    @CachePut(value = "users", key = "#userDTO.id")
    public UserDTO update(UserDTO userDTO) {
        // 更新數(shù)據(jù)庫(kù)邏輯
    }
    
    // 刪除時(shí)清除緩存
    @CacheEvict(value = "users", key = "#id")
    public void delete(Long id) {
        // 刪除數(shù)據(jù)庫(kù)記錄邏輯
    }
}

對(duì)于更復(fù)雜的場(chǎng)景,比如熱點(diǎn)數(shù)據(jù)緩存、緩存預(yù)熱,我們可以實(shí)現(xiàn)自定義緩存管理器,在系統(tǒng)啟動(dòng)時(shí)預(yù)加載常用數(shù)據(jù)到緩存中。

異步處理:不阻塞主線程

對(duì)于耗時(shí)操作(如發(fā)送郵件、生成報(bào)表),我們不應(yīng)該讓 API 調(diào)用者一直等待,可以用異步處理來(lái)提升響應(yīng)速度:

@Configuration
@EnableAsync
publicclass AsyncConfig {
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 核心線程數(shù)
        executor.setMaxPoolSize(10); // 最大線程數(shù)
        executor.setQueueCapacity(25); // 隊(duì)列容量
        executor.setThreadNamePrefix("async-"); // 線程名前綴
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

@Service
publicclass NotificationService {
    // 標(biāo)記為異步方法
    @Async("taskExecutor")
    public CompletableFuture<Void> sendWelcomeEmail(String email) {
        // 發(fā)送郵件的耗時(shí)操作
        return CompletableFuture.runAsync(() -> {
            // 實(shí)際發(fā)送邏輯
            try {
                Thread.sleep(1000); // 模擬耗時(shí)操作
                log.info("發(fā)送歡迎郵件到: {}", email);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

// 在Controller中使用
@PostMapping("/users")
public ApiResponse<UserDTO> createUser(@Valid @RequestBody UserCreateDTO userDTO) {
    UserDTO created = userService.create(userDTO);
    // 異步發(fā)送郵件,不阻塞當(dāng)前請(qǐng)求
    notificationService.sendWelcomeEmail(created.getEmail());
    return ApiResponse.success(created);
}

這樣處理后,用戶注冊(cè)接口的響應(yīng)時(shí)間不會(huì)受到發(fā)送郵件耗時(shí)的影響,大大提升了用戶體驗(yàn)。

七、實(shí)戰(zhàn)經(jīng)驗(yàn):優(yōu)雅不是炫技

最后我想分享一些實(shí)戰(zhàn)經(jīng)驗(yàn)。優(yōu)雅的 API 不是炫技,而是解決實(shí)際問(wèn)題的智慧。過(guò)度設(shè)計(jì)的 API 就像裝修過(guò)度的房子,華而不實(shí),維護(hù)成本還高。

保持簡(jiǎn)單

能用 GET 請(qǐng)求解決的問(wèn)題,就不要用 POST;能少傳一個(gè)參數(shù),就不要多傳。記住,每個(gè)額外的參數(shù)都是給調(diào)用者的負(fù)擔(dān)。

向后兼容

API 升級(jí)時(shí)一定要保持向后兼容。不要輕易刪除字段或改變參數(shù)含義,可以新增字段或新增版本,給調(diào)用者足夠的遷移時(shí)間。

監(jiān)控與日志

優(yōu)雅的 API 需要完善的監(jiān)控和日志。通過(guò) AOP 記錄接口的調(diào)用情況、響應(yīng)時(shí)間、異常信息,這樣出現(xiàn)問(wèn)題時(shí)才能快速定位:

@Aspect
@Component
@Slf4j
publicclass ApiMonitorAspect {
    @Around("execution(* com.example.api.controller..*(..)) && @annotation(requestMapping)")
    public Object monitorApi(ProceedingJoinPoint joinPoint, RequestMapping requestMapping) throws Throwable {
        long start = System.currentTimeMillis();
        String method = Arrays.stream(requestMapping.method())
                .map(HttpMethod::name)
                .findFirst()
                .orElse("UNKNOWN");
        String path = Arrays.stream(requestMapping.value()).findFirst().orElse("");
        
        try {
            Object result = joinPoint.proceed();
            long cost = System.currentTimeMillis() - start;
            log.info("API調(diào)用: {} {} 耗時(shí): {}ms", method, path, cost);
            return result;
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - start;
            log.error("API異常: {} {} 耗時(shí): {}ms", method, path, cost, e);
            throw e;
        }
    }
}

結(jié)語(yǔ)

寫優(yōu)雅的 API 就像做一道好菜,食材(技術(shù))重要,火候(經(jīng)驗(yàn))重要,用心(態(tài)度)更重要。一個(gè)優(yōu)雅的 API 能體現(xiàn)開發(fā)者的專業(yè)素養(yǎng),也能讓整個(gè)團(tuán)隊(duì)的協(xié)作效率事半功倍。

希望這篇文章能給你帶來(lái)啟發(fā)。下次寫 API 的時(shí)候,不妨多站在調(diào)用者的角度想一想:這個(gè)接口好懂嗎?好用嗎?安全嗎?高效嗎?

責(zé)任編輯:武曉燕 來(lái)源: 石杉的架構(gòu)筆記
相關(guān)推薦

2020-11-03 16:00:33

API接口微服務(wù)框架編程語(yǔ)言

2020-11-17 09:34:31

API接口后端

2022-12-12 08:14:47

2025-07-14 00:00:00

接口重試MQTT冪等性

2025-03-11 08:20:58

2025-05-30 08:20:54

2024-11-12 08:20:31

2025-04-08 08:20:33

2024-10-24 08:21:33

2024-12-02 00:59:30

Spring

2025-04-22 08:20:51

2025-03-06 08:21:02

判空entity對(duì)象

2025-02-28 08:21:00

2023-12-30 20:04:51

MyBatis框架數(shù)據(jù)

2024-10-17 09:21:30

2019-12-04 09:54:03

深度學(xué)習(xí)編程人工智能

2025-04-02 12:20:00

開發(fā)代碼函數(shù)

2024-11-07 10:55:26

2024-11-08 15:56:36

2022-06-21 14:44:38

接口數(shù)據(jù)脫敏
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

中文字幕人成人乱码亚洲电影| 国产欧美视频一区| h视频网站在线观看| 看电视剧不卡顿的网站| 欧美成人四级hd版| 亚洲天堂2024| av成人在线观看| 亚洲在线成人精品| 亚洲精品成人三区| 丰满人妻一区二区三区免费视频| 99热在线精品观看| 色偷偷88888欧美精品久久久| 国产无套精品一区二区三区| 日日夜夜天天综合| 夜夜精品浪潮av一区二区三区| 欧美精品尤物在线| 成 人 免费 黄 色| 免费在线观看一区二区三区| 国语自产精品视频在免费| 亚洲最大成人综合网| av综合网页| 欧美日韩国产大片| av动漫在线观看| 性网站在线观看| 国产精品美女视频| 免费观看成人高| 后进极品白嫩翘臀在线视频| 久草热8精品视频在线观看| 2019中文字幕免费视频| a级黄色片免费看| 四虎成人精品永久免费av九九| 亚洲精品国产福利| 国产精品91av| 亚洲网站三级| 欧美在线免费播放| 人体内射精一区二区三区| 福利在线视频网站| 国产精品国产三级国产普通话三级| 亚洲免费黄色| 精品久久久中文字幕人妻| 九九视频免费在线观看| 成人mm视频在线观看| 午夜av一区二区三区| 91国在线高清视频| 中文国产字幕在线观看| 国产精品久久久久久久岛一牛影视 | 欧美日韩极品在线观看一区| 四虎4hu永久免费入口| bbbbbbbbbbb在线视频| 久久无码av三级| 久久精品午夜一区二区福利| 婷婷在线观看视频| av在线这里只有精品| 国产精品一区二区a| 亚洲av永久无码国产精品久久| 国产乱码精品一区二区三| 91久久久久久久一区二区| 一级黄在线观看| 精品一区免费av| 91在线观看免费网站| 一级片一区二区三区| 久久精品国产亚洲高清剧情介绍| 国产精品久久久久久久久久东京| 国产污视频网站| 日本va欧美va精品| 国产综合久久久久久| 国产又黄又粗又硬| 岛国av在线一区| 国产亚洲欧美一区二区三区| 天堂av2024| 久久婷婷综合激情| 亚洲精品中字| 二区在线播放| 性做久久久久久免费观看 | yjizz国产| 久久久久久女乱国产| 国产黄色精品网站| 国产精品久久久久久久久久久久午夜片| 精品人妻伦一区二区三区久久| 国产福利91精品一区二区三区| 国产精品欧美久久| 国模吧精品人体gogo| 国产精品久久久久久久久免费樱桃| 曰韩不卡视频| а√在线中文网新版地址在线| 欧美日韩在线免费观看| 午夜视频你懂的| 欧美成年网站| 亚洲男人第一网站| 成人欧美一区二区三区黑人一| 欧美涩涩视频| 欧美专区国产专区| 国产真实乱子伦| 六月丁香激情综合| 亚洲免费综合| 成人午夜一级二级三级| 三级网站在线看| 国产日韩欧美高清| 成人短视频在线观看免费| 亚洲电影观看| 欧美一区二区播放| 波多野结衣片子| 欧美黄色一级视频| 琪琪第一精品导航| 国产av无码专区亚洲av麻豆| 91蜜桃网址入口| 国产成人免费高清视频| 美女100%一区| 日韩欧美一级片| 女人又爽又黄免费女仆| 欧美国产91| 国产精品白嫩美女在线观看| 不卡视频在线播放| 国产精品久久久久久亚洲伦| 久久久久免费看黄a片app| 午夜精品久久久久久毛片| 亚洲国产中文字幕久久网| 少妇视频一区二区| 米奇777在线欧美播放| 亚洲一区二区三区成人在线视频精品 | 国产亚av手机在线观看| 色中色一区二区| 在线观看免费不卡av| 日韩激情网站| 九九热最新视频//这里只有精品 | 波多野结衣中文在线| 777a∨成人精品桃花网| 国产交换配乱淫视频免费| 亚洲视频观看| 99re国产在线播放| 欧美日韩在线看片| 欧美日韩亚洲一区二| 欧美性生交xxxxx| 欧美日韩理论| 91久久国产综合久久蜜月精品| av在线之家电影网站| 色一情一乱一乱一91av| 中文字幕av网址| 99成人免费视频| 国产欧美日韩伦理| 毛片在线网址| 精品国内二区三区| 欧美精品99久久久| 国产成人一区在线| wwwjizzjizzcom| 日韩精品中文字幕一区二区| 爱福利视频一区| 一区二区三区黄| 国产精品每日更新在线播放网址| www.超碰com| 国内精品久久久久久久久电影网 | 色妞ww精品视频7777| 久久九九免费视频| 99久久国产免费| 一区二区在线免费| 又黄又色的网站| 亚洲国产黄色| 欧美在线播放一区| 福利一区二区免费视频| www.亚洲天堂| 精品女同一区二区三区| 亚洲一区二区在线视频| 免费日本黄色网址| 久久精品道一区二区三区| 日产精品高清视频免费| 四虎影视成人精品国库在线观看 | 国产综合久久久久久鬼色 | 欧美精品xxxxbbbb| 神马午夜精品91| 久久不见久久见免费视频7| 亚洲精品中文字幕| 成人综合激情网| 久久久久99精品成人片| 国产精品videossex| 亚洲人精品午夜| 成人性生活视频免费看| 日本一区福利在线| 国产精品白嫩美女在线观看| 99免在线观看免费视频高清| 欧美二区三区91| 久久久久久蜜桃| 91免费看片在线观看| www.涩涩涩| 国一区二区在线观看| 久久综合毛片| 亚洲伊人精品酒店| 97精品伊人久久久大香线蕉 | 精品一区二区三区不卡 | 在线heyzo| 亚洲精品小视频| 国产女人18毛片水18精| 午夜天堂影视香蕉久久| 人妻一区二区视频| 国产激情偷乱视频一区二区三区| 91传媒久久久| 亚洲中无吗在线| 欧美一卡2卡3卡4卡无卡免费观看水多多 | 91精品视频免费观看| 爱福利在线视频| 日韩在线视频观看| 青青国产在线| 日韩欧美视频一区| 五月激情丁香网| 亚洲电影第三页| 三上悠亚作品在线观看| xfplay精品久久| 国产免费无码一区二区| 久久国产精品99久久久久久老狼| 日本韩国欧美在线观看| 在线看片不卡| 日韩精品欧美一区二区三区| 9l视频自拍九色9l视频成人| 国产欧美欧洲在线观看| 在线看的毛片| 欧美激情奇米色| 黄色一级大片在线免费看产| 亚洲午夜小视频| 五月婷中文字幕| 日韩免费视频线观看| 最好看的日本字幕mv视频大全| 精品成人国产在线观看男人呻吟| 真实国产乱子伦对白在线| 国产精品久久久久久久久久久免费看| 亚洲最大成人网站| 成人国产精品免费观看动漫| 图片区乱熟图片区亚洲| 久久99精品久久只有精品| 女性隐私黄www网站视频| 国产日韩欧美在线播放不卡| 免费的一级黄色片| 亚洲色图二区| 男同互操gay射视频在线看| 久久美女精品| 亚洲高清精品中出| 国产一区二区观看| 欧美一区少妇| 国产亚洲一区| 天堂资源在线亚洲视频| 国产一区不卡| 亚洲精品在线视频观看| 成人直播大秀| 亚洲人一区二区| 日韩电影一区| 亚洲一区二区三区色| 水蜜桃久久夜色精品一区| 伊甸园精品99久久久久久| 四虎成人av| 国产又粗又爽又黄的视频| 久久精品青草| 一级黄色片播放| 欧美精品99| 男的插女的下面视频| 亚洲国产高清一区二区三区| 狠狠干 狠狠操| 亚洲在线免费| 久久综合伊人77777麻豆最新章节| 丝袜诱惑亚洲看片| 亚洲精品综合在线观看| 国产中文字幕一区| 蜜桃视频无码区在线观看| 成人av影院在线| 国产精品300页| 国产片一区二区三区| 久久视频精品在线观看| 中文字幕欧美激情| 黑鬼狂亚洲人videos| 亚洲香蕉伊在人在线观| 可以免费看的av毛片| 91福利在线免费观看| 亚洲无码久久久久久久| 91精品欧美福利在线观看| www.色婷婷.com| 亚洲国产一区二区三区在线观看| 国产日产精品久久久久久婷婷| 中文字幕日韩在线观看| 成人ww免费完整版在线观看| 久久久噜噜噜久久| 婷婷激情一区| 91|九色|视频| 色天天色综合| 婷婷视频在线播放| 一本久道久久久| 牛夜精品久久久久久久| 国产传媒日韩欧美成人| 熟女少妇一区二区三区| 成人免费在线观看入口| 日本中文字幕免费| 欧美人妇做爰xxxⅹ性高电影| 亚洲av无码乱码国产麻豆| 亚洲午夜久久久久久久| 牛牛精品在线| 国产精品免费视频xxxx| 白白在线精品| 亚洲高清精品中出| 国产精品一二| 中文字幕在线观看视频www| 久久综合给合久久狠狠狠97色69| 希岛爱理中文字幕| 在线一区二区视频| 六月丁香综合网| 日韩在线不卡视频| 最新日韩精品| 不卡视频一区二区| 成人区精品一区二区婷婷| 丝袜人妻一区二区三区| 久久精品国产一区二区三| 国产色视频一区二区三区qq号| 亚洲欧美日韩成人高清在线一区| 青青视频在线免费观看| 精品美女在线观看| 成人在线播放| 国产免费一区视频观看免费| 亚洲婷婷影院| 国产a级片网站| 国产成人午夜精品5599| 99国产精品无码| 91国产成人在线| 男人天堂资源在线| 91sa在线看| 草莓视频一区二区三区| 亚洲第一综合网站| 青娱乐精品视频| 亚洲精品午夜视频| 欧美日韩亚洲视频一区| 三级视频在线看| 欧美极品在线播放| 亚洲综合影院| 日本中文字幕一级片| 国内精品久久久久影院色| 亚洲欧洲综合网| 欧美久久久久久蜜桃| a√资源在线| 国产精品久久久久不卡| 国产精品一区高清| 国产a视频免费观看| 26uuu亚洲| 日本免费精品视频| 夜夜嗨av色综合久久久综合网| 三级成人黄色影院| 欧洲亚洲一区| 免费看欧美女人艹b| xxxx日本黄色| 欧美日韩精品一区二区天天拍小说| 国产在线播放av| 国产精品91久久久| 青青草综合网| 中文字幕 91| 国产精品久久久久国产精品日日| 中文字字幕在线观看| 日韩中文字幕在线视频播放| 久久亚洲精品人成综合网| 在线观看一区欧美| 国产主播一区二区三区| 久久久精品国产sm调教网站| 精品国产一区二区三区久久久蜜月 | 欧美孕妇与黑人孕交| 国产精品免费大片| 天天操狠狠操夜夜操| 一区二区三区欧美| 亚洲av片一区二区三区| 日韩美女av在线免费观看| 日本成人小视频| 日韩av福利在线观看| 亚洲国产欧美在线| 男女视频在线观看免费| 国产精品视频免费观看www| 婷婷激情综合| 涩视频在线观看| 色婷婷精品久久二区二区蜜臀av| 超碰国产在线观看| 91精品在线播放| 亚洲国产高清一区二区三区| 91成年人网站| 欧美一级搡bbbb搡bbbb| 9999精品成人免费毛片在线看| 免费h精品视频在线播放| 久久精品国产久精国产| 久久久国产精品人人片| 亚洲跨种族黑人xxx| 欧洲亚洲精品久久久久| 久草视频这里只有精品| 久久久亚洲国产美女国产盗摄| 一级黄色免费片| 高清一区二区三区日本久| 国产欧美高清视频在线| 亚洲av毛片在线观看| 欧美色另类天堂2015| 国产黄色小视频在线| 久久一区二区三区欧美亚洲| 国产综合一区二区| 久久久久久久久久影院| 日日骚av一区| 乱亲女h秽乱长久久久| www.色就是色.com| 狠狠躁夜夜躁人人躁婷婷91 | 天堂v在线观看| 国产欧美日韩最新| 久久精品一区二区国产| 久久久久久久久久久久久久久久久| 精品一区二区三区三区|