【问题标题】:SonarLint: Return an empty collection instead of nullSonarLint:返回一个空集合而不是 null
【发布时间】:2020-05-15 03:34:17
【问题描述】:

我正在对返回对象列表的方法进行 ajax 调用,如果在 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;        
}

知道如何解决这个通知吗?

【问题讨论】:

  • if something happens while getting the data in a try-catch block I have a response.setStatus(400) - 4xx 错误是与客户端相关的问题,您应该返回 5xx。让异常传播并通过@ControllerAdvice 连接异常映射器可能更好

标签: java spring-boot sonarlint


【解决方案1】:

我建议不要在此方法中捕获异常,而是将其抛出,并在控制器中使用exception handler method 来处理它。在这种情况下,您将永远不会从该方法返回 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 参数。

【讨论】:

    【解决方案2】:

    我建议您创建一个 GenericReponse 来包装您的所有响应,这对于前端来说非常好,因为您面对的是固定模板。

    因此,通过此解决方案,您可以包装任何您想要的对象并将其发送到响应中。


    我这样编写场景:

    1- 创建一个 GenericResponse 类

    @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-创建ErrorPayload类

    @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. 创建上述类(复制我在 1、2 和 3 中编写的代码)
    2. 将您的回复表单List&lt;Map&lt;String, Object&gt;&gt; 更改为GenericResponse
    3. 将您的返回类型包装到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"
            }
        ]
    }
    

    【讨论】:

    • 这不是Serializable 的用途。 Serializable 用于 Java 二进制序列化,它不是用于也不需要序列化为 json 的。使用Object 作为payload 方法的参数类型就足够了。
    猜你喜欢
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 2011-05-21
    • 1970-01-01
    • 2020-02-23
    • 2018-01-17
    • 1970-01-01
    • 2010-12-24
    相关资源
    最近更新 更多