SonarLint:Return 一个空集合而不是 null
SonarLint: Return an empty collection instead of null
我正在 ajax 调用一个 returns 对象列表的方法,如果在 try-catch 块中获取数据时发生某些事情,我有一个 response.setStatus(400)
然后在前端显示错误,在那里我返回 null,在那里我收到 SonarLint 通知。现在,如果我将其更改为空集合,则会出现以下错误:
getWriter() has already been called for this response
我认为以上是因为我返回的是空集合和 HTTP 响应状态 400。如果我将其保留为空,则一切正常,只是 SonarLint 通知。
@GetMapping("/runquery")
@ResponseBody
public List<Map<String, Object>> runQuery(@RequestParam(name = "queryId") String queryId, @RequestParam(name = "formData") String formData, HttpServletResponse response) throws IOException {
(...)
try {
queryResult = namedParameterJdbcTemplateHive.queryForList(query, paramSource);
for (Map<String, Object> map : queryResult) {
Map<String, Object> newMap = new HashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (key.contains(".")) {
key = key.replace(".", "_");
newMap.put(key, value);
} else {
newMap.put(key, value);
}
}
queryResultFinal.add(newMap);
}
} catch (Exception e) {
response.setStatus(400);
response.getWriter().write(e.getMessage());
return null; <-- SonarLint notification
}
return queryResultFinal;
}
知道如何修复此通知吗?
我建议不要在此方法中捕获异常,而是抛出它,并在控制器中使用 exception handler method 来处理它。在那种情况下,你永远不会 return null
从方法中,Sonar 将没有什么可抱怨的。这也意味着您正在按照设计使用的方式使用 Spring。
例如,如下所示:
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleException(Exception e) {
log.error("Exception during request", e);
}
或您当前处理的直接等价物:
@ExceptionHandler
public ResponseEntity<?> handleException(Exception e) {
return ResponseEntity.badRequest().body(e.getMessage()).build();
}
切换到异常处理程序后,您可以从常规方法中删除 HttpServletResponse response
参数。
我建议你创建一个 GenericReponse 来包装你的所有回复,这对前端来说非常好,因为你面对的是一个固定的模板。
因此,通过这个解决方案,您可以包装任何您想要的对象并将其发送到响应。
我这样编写场景:
1- 创建一个通用响应 Class
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class GenericResponse {
private Boolean error;
private List<ErrorPayload> errorPayload;
private Object payload;
public GenericResponse(Boolean error) {
this.error = error;
}
public static GenericResponse ok() {
return new GenericResponse(false);
}
public GenericResponse payload(Serializable o) {
this.payload = o;
return this;
}
//Getters and Setters and other Constructors
2-创建ErrorPayloadClass
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorPayload {
private String errorMessage;
private String errorType;
//Getters and Setters and Constructors
}
3-创建ExceptionConverter服务(当我们有异常时使用)
@Service
public class ExceptionConverterService {
public GenericResponse convert(Exception x) {
GenericResponse genericResponse = new GenericResponse();
genericResponse.setError(true);
String exceptionType = x.getClass().getSimpleName();
String exceptionMessage = x.getClass().getSimpleName();
genericResponse.setErrorPayload(Collections.singletonList(new ErrorPayload(exceptionType, exceptionMessage)));
return genericResponse;
}
}
4-使用 GenericResponse 改变您的场景
您只需:
- 创建上述类(复制我在1、2、3中写的代码)
- 将您的回复表单
List<Map<String, Object>>
更改为 GenericResponse
- 将您的 return 类型包装成
GenericResponse
我更改了你的代码如下(只需更改 3 行)
@RestController
public class TestController {
@Autowired
private ExceptionConverterService exceptionConverter;
@GetMapping("/runquery")
@ResponseBody
//Changed (Change Return type to GenericResponse )
public GenericResponse runQuery(@RequestParam(name = "queryId") String queryId, @RequestParam(name = "formData") String formData, HttpServletResponse response) throws IOException {
try {
//Your code
}
} catch (Exception e) {
//Changed (Create GenericResponse for Exception)
GenericResponse genericResponse = exceptionConverter.convert(e);
return genericResponse;
}
//Changed (Create GenericResponse for main result)
return GenericResponse.ok().payload(queryResultFinal);
}
}
两种情况的示例(第一种情况无例外,第二种情况有例外)
示例 1
带有 GenericResponse 的控制器(我们在这个示例中也不例外)
@RestController
public class TestController {
@GetMapping(value = "/getNameAndFamily")
public GenericResponse getNameAndFamily() {
Map<String, String> person = new HashMap<>();
person.put("name", "foo");
person.put("family", "bar");
return GenericResponse.ok().payload((Serializable) person);
}
}
结果如下:
{
"error": false,
"payload": {
"name": "foo",
"family": "bar"
}
}
示例 2
当我们在业务中遇到异常时使用 GenericResponse 的控制器
@RestController
public class TestController {
@Autowired
private ExceptionConverterService exceptionConverter;
@GetMapping(value = "/getNameAndFamily")
public GenericResponse getNameAndFamily() {
try {
//Create Fake Exception
int i = 1 / 0;
return GenericResponse.ok();
} catch (Exception e) {
//Handle Exception
GenericResponse genericResponse = exceptionConverter.convert(e);
return GenericResponse.ok().payload((Serializable) genericResponse);
}
}
}
结果如下:
{
"error": true,
"errorPayload": [
{
"errorType": "ArithmeticException"
}
]
}
我正在 ajax 调用一个 returns 对象列表的方法,如果在 try-catch 块中获取数据时发生某些事情,我有一个 response.setStatus(400)
然后在前端显示错误,在那里我返回 null,在那里我收到 SonarLint 通知。现在,如果我将其更改为空集合,则会出现以下错误:
getWriter() has already been called for this response
我认为以上是因为我返回的是空集合和 HTTP 响应状态 400。如果我将其保留为空,则一切正常,只是 SonarLint 通知。
@GetMapping("/runquery")
@ResponseBody
public List<Map<String, Object>> runQuery(@RequestParam(name = "queryId") String queryId, @RequestParam(name = "formData") String formData, HttpServletResponse response) throws IOException {
(...)
try {
queryResult = namedParameterJdbcTemplateHive.queryForList(query, paramSource);
for (Map<String, Object> map : queryResult) {
Map<String, Object> newMap = new HashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (key.contains(".")) {
key = key.replace(".", "_");
newMap.put(key, value);
} else {
newMap.put(key, value);
}
}
queryResultFinal.add(newMap);
}
} catch (Exception e) {
response.setStatus(400);
response.getWriter().write(e.getMessage());
return null; <-- SonarLint notification
}
return queryResultFinal;
}
知道如何修复此通知吗?
我建议不要在此方法中捕获异常,而是抛出它,并在控制器中使用 exception handler method 来处理它。在那种情况下,你永远不会 return null
从方法中,Sonar 将没有什么可抱怨的。这也意味着您正在按照设计使用的方式使用 Spring。
例如,如下所示:
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleException(Exception e) {
log.error("Exception during request", e);
}
或您当前处理的直接等价物:
@ExceptionHandler
public ResponseEntity<?> handleException(Exception e) {
return ResponseEntity.badRequest().body(e.getMessage()).build();
}
切换到异常处理程序后,您可以从常规方法中删除 HttpServletResponse response
参数。
我建议你创建一个 GenericReponse 来包装你的所有回复,这对前端来说非常好,因为你面对的是一个固定的模板。
因此,通过这个解决方案,您可以包装任何您想要的对象并将其发送到响应。
我这样编写场景:
1- 创建一个通用响应 Class
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class GenericResponse {
private Boolean error;
private List<ErrorPayload> errorPayload;
private Object payload;
public GenericResponse(Boolean error) {
this.error = error;
}
public static GenericResponse ok() {
return new GenericResponse(false);
}
public GenericResponse payload(Serializable o) {
this.payload = o;
return this;
}
//Getters and Setters and other Constructors
2-创建ErrorPayloadClass
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorPayload {
private String errorMessage;
private String errorType;
//Getters and Setters and Constructors
}
3-创建ExceptionConverter服务(当我们有异常时使用)
@Service
public class ExceptionConverterService {
public GenericResponse convert(Exception x) {
GenericResponse genericResponse = new GenericResponse();
genericResponse.setError(true);
String exceptionType = x.getClass().getSimpleName();
String exceptionMessage = x.getClass().getSimpleName();
genericResponse.setErrorPayload(Collections.singletonList(new ErrorPayload(exceptionType, exceptionMessage)));
return genericResponse;
}
}
4-使用 GenericResponse 改变您的场景
您只需:
- 创建上述类(复制我在1、2、3中写的代码)
- 将您的回复表单
List<Map<String, Object>>
更改为GenericResponse
- 将您的 return 类型包装成
GenericResponse
我更改了你的代码如下(只需更改 3 行)
@RestController
public class TestController {
@Autowired
private ExceptionConverterService exceptionConverter;
@GetMapping("/runquery")
@ResponseBody
//Changed (Change Return type to GenericResponse )
public GenericResponse runQuery(@RequestParam(name = "queryId") String queryId, @RequestParam(name = "formData") String formData, HttpServletResponse response) throws IOException {
try {
//Your code
}
} catch (Exception e) {
//Changed (Create GenericResponse for Exception)
GenericResponse genericResponse = exceptionConverter.convert(e);
return genericResponse;
}
//Changed (Create GenericResponse for main result)
return GenericResponse.ok().payload(queryResultFinal);
}
}
两种情况的示例(第一种情况无例外,第二种情况有例外)
示例 1
带有 GenericResponse 的控制器(我们在这个示例中也不例外)
@RestController
public class TestController {
@GetMapping(value = "/getNameAndFamily")
public GenericResponse getNameAndFamily() {
Map<String, String> person = new HashMap<>();
person.put("name", "foo");
person.put("family", "bar");
return GenericResponse.ok().payload((Serializable) person);
}
}
结果如下:
{
"error": false,
"payload": {
"name": "foo",
"family": "bar"
}
}
示例 2
当我们在业务中遇到异常时使用 GenericResponse 的控制器
@RestController
public class TestController {
@Autowired
private ExceptionConverterService exceptionConverter;
@GetMapping(value = "/getNameAndFamily")
public GenericResponse getNameAndFamily() {
try {
//Create Fake Exception
int i = 1 / 0;
return GenericResponse.ok();
} catch (Exception e) {
//Handle Exception
GenericResponse genericResponse = exceptionConverter.convert(e);
return GenericResponse.ok().payload((Serializable) genericResponse);
}
}
}
结果如下:
{
"error": true,
"errorPayload": [
{
"errorType": "ArithmeticException"
}
]
}