如何正确校验BankId接口返回值非空?最佳实践探讨
BankId认证轮询方法的非空校验优化方案
我编写了BankId认证轮询回调结果的方法如下:
public boolean authenticateAndPollCallbackResult(BankIdAuthRequest bankIdAuthRequest) { ResponseEntity<BankIdAuthResponse> authResponse = bankIdAuthentication(bankIdAuthRequest); AbstractApplicationForm applicationForm = applicationFormRepository.findByToken(bankIdAuthRequest.getRefID()); try { //Add new bankId authentication to database. BankIdAuthenticationEntity bankIdAuthenticationEntity = new BankIdAuthenticationEntity(); bankIdAuthenticationEntity.setAbstractApplicationForm(applicationForm); bankIdAuthenticationEntity.setAuthStatus(STATUS_PROGRESS); bankIdAuthenticationEntity.setOrderReference(authResponse.getBody().getOrderRef()); bankIdAuthenticationEntity.setAutoStartToken(authResponse.getBody().getAutoStartToken()); Long bankIdAuthenticationId = bankIdAuthenticationRepository.save(bankIdAuthenticationEntity).getId(); BankIdAuthenticationEntity.AuthStatus authStatus; do { TimeUnit.MILLISECONDS.sleep(1500); authStatus = getAuthStatus(bankIdAuthenticationId); if (authStatus == BankIdAuthenticationEntity.AuthStatus.COMPLETED) return true; if (authStatus == BankIdAuthenticationEntity.AuthStatus.FAILED || authStatus == BankIdAuthenticationEntity.AuthStatus.NOT_ASSIGNED) return false; } while (authStatus == BankIdAuthenticationEntity.AuthStatus.PROGRESS); } catch (InterruptedException e) { log.error("InterruptedException: ", e); Thread.currentThread().interrupt(); } catch (NullPointerException e) { log.error("Either BankId API not responding correctly. Check server connection", e); } catch (Exception e) { log.error("Exception: Polling collect endpoint method failed", e); } return false; }
SonarQube告警以下两行存在空指针风险(authResponse.getBody()或内部方法可能返回null):
bankIdAuthenticationEntity.setOrderReference(authResponse.getBody().getOrderRef()); bankIdAuthenticationEntity.setAutoStartToken(authResponse.getBody().getAutoStartToken());
我尝试用Objects.requireNonNull抛出空指针并捕获,但感觉不够优雅合理,以下是几种更合适的非空校验方案:
方案一:提前校验+快速失败(推荐)
遵循fail-fast原则,在使用响应数据前提前校验所有可能为空的节点,抛出明确的业务异常而非依赖NullPointerException,既消除SonarQube告警,也让错误原因更清晰。
ResponseEntity<BankIdAuthResponse> authResponse = bankIdAuthentication(bankIdAuthRequest); AbstractApplicationForm applicationForm = applicationFormRepository.findByToken(bankIdAuthRequest.getRefID()); // 提前校验响应及响应体非空 if (authResponse == null || authResponse.getBody() == null) { throw new BankIdApiException("BankId API未返回有效响应,请检查服务连接"); } BankIdAuthResponse responseBody = authResponse.getBody(); // 校验必要业务字段非空 if (responseBody.getOrderRef() == null) { throw new BankIdApiException("BankId API响应缺少必要字段:orderRef"); } if (responseBody.getAutoStartToken() == null) { throw new BankIdApiException("BankId API响应缺少必要字段:autoStartToken"); } // 后续安全使用字段 bankIdAuthenticationEntity.setOrderReference(responseBody.getOrderRef()); bankIdAuthenticationEntity.setAutoStartToken(responseBody.getAutoStartToken());
注:需自定义BankIdApiException等业务异常类,替代通用的NPE捕获,让错误处理更精准。
方案二:使用Optional链式处理空值
利用Java 8+的Optional封装可能为空的对象,通过链式调用简化空值判断,同时可以灵活指定空值时的处理逻辑(抛出异常或设置默认值)。
ResponseEntity<BankIdAuthResponse> authResponse = bankIdAuthentication(bankIdAuthRequest); AbstractApplicationForm applicationForm = applicationFormRepository.findByToken(bankIdAuthRequest.getRefID()); // 处理响应体为空的情况 BankIdAuthResponse responseBody = Optional.ofNullable(authResponse) .map(ResponseEntity::getBody) .orElseThrow(() -> new BankIdApiException("BankId API响应为空")); // 处理字段为空的情况 String orderRef = Optional.ofNullable(responseBody.getOrderRef()) .orElseThrow(() -> new BankIdApiException("BankId响应缺少orderRef字段")); String autoStartToken = Optional.ofNullable(responseBody.getAutoStartToken()) .orElseThrow(() -> new BankIdApiException("BankId响应缺少autoStartToken字段")); // 安全赋值 bankIdAuthenticationEntity.setOrderReference(orderRef); bankIdAuthenticationEntity.setAutoStartToken(autoStartToken);
这种方式代码可读性更强,空值处理逻辑更直观,也能满足SonarQube的非空校验要求。
方案三:业务允许时设置默认值
如果业务逻辑允许orderRef或autoStartToken为空,可以为其设置合理的默认值(如空字符串),避免抛出异常。
ResponseEntity<BankIdAuthResponse> authResponse = bankIdAuthentication(bankIdAuthRequest); AbstractApplicationForm applicationForm = applicationFormRepository.findByToken(bankIdAuthRequest.getRefID()); // 空响应体时创建默认对象(需确保BankIdAuthResponse有无参构造) BankIdAuthResponse responseBody = Optional.ofNullable(authResponse) .map(ResponseEntity::getBody) .orElse(new BankIdAuthResponse()); // 字段为空时设置默认值 String orderRef = Optional.ofNullable(responseBody.getOrderRef()).orElse(""); String autoStartToken = Optional.ofNullable(responseBody.getAutoStartToken()).orElse(""); // 赋值 bankIdAuthenticationEntity.setOrderReference(orderRef); bankIdAuthenticationEntity.setAutoStartToken(autoStartToken);
注意:此方案仅适用于业务允许字段为空的场景,否则会隐藏数据异常问题。
额外建议
避免在catch块中捕获通用的NullPointerException,NPE本质是编程错误,应通过提前校验从根源避免。改用捕获自定义业务异常,能更精准地处理不同场景的错误,也便于后续排查问题。
内容的提问来源于stack exchange,提问作者Mr.Gomer
相关产品推荐
相关产品推荐

