【问题标题】:SpringBoot: Figuring out type of the field in inheritanceSpringBoot:找出继承中字段的类型
【发布时间】:2021-01-27 10:11:58
【问题描述】:

正如this answer 中提到的,我已经编写了如下所示的 DTO

class Car { 
    ... 
    private TransmissionType transmissionType;
    
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "transmissionType")
    private Transmission transmission; 
}


@JsonSubTypes({
        @JsonSubTypes.Type(value = AutomaticTransmission.class, name = "AUTOMATIC"),
        @JsonSubTypes.Type(value = ManualTransmission.class, name = "MANUAL")
})
public abstract class Transmission {
}

public class AutomaticTransmission {
     public Technology technology; // DCT/CVT/AMT
}

public class ManualTransmission {
     public int numGears; 
}

现在,在执行POST /api/v1/cars 时,用户可以发送transmissionType,但在编辑汽车时,即PATCH /api/v1/cars/{id},要求用户发送汽车类型有点奇怪,因为他们已经创建了汽车并且服务应该知道它。我在想如何在不询问用户的情况下推断出汽车的类型。

一个解决方案,我正在考虑编写一些拦截器,它将获取路径参数id 并从数据库中找出汽车的类型并以这种方式插入为transmissionType,而无需用户传递transmissionType控制器会得到它。

第二个解决方案是获取带有汽车的 json body,但使用这种方法,用户将看不到强类型,并且需要手动完成验证。

我应该选择什么方法?有没有其他更好的方法来解决这个问题?

【问题讨论】:

    标签: java spring-boot jackson


    【解决方案1】:

    嗯,这取决于您如何理解 PATCH 请求。如果您总是希望请求包含整个实体(在我看来更像是 PUT,而不是 PATCH)而不是肯定的,那么即使没有更改,用户也需要包含 transmissionType。我认为这很自然。否则怎么知道类型没有变呢?

    另一方面,使用“真实”补丁,用户只需发送更改的字段,您需要修补从数据库中检索到的实体。在这种情况下,如果不更改 transmissionType,则不需要它。

    我认为将transmissionType 包含在Transmission 对象中而不是Car 会更自然(将include 更改为As.PROPERTY)。然后,在 PATCH 期间,用户要么根本不发送 transmission(没有更改),要么使用 transmissionType 发送。

    【讨论】:

    • 即使我们将其添加到Transmission,问题仍然存在于PATCH /api/v1/cars/{id}/transmission 资源中。
    【解决方案2】:

    我最终解决了以下问题

    public class TypeInferenceRequestWrapper extends HttpServletRequestWrapper {
        // https://stackoverflow.com/a/63073783/2870572
        private final String body;
    
        public TypeInferenceRequestWrapper(HttpServletRequest request) throws IOException {
            // So that other request method behave just like before
            super(request);
    
            String requestBody = readBody(request);
    
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = objectMapper.readTree(requestBody);
    
            String requestURI = request.getRequestURI();
            String requestMethod = request.getMethod();
    
            if (jsonNode.getNodeType() == JsonNodeType.OBJECT) {
                Map.Entry<String, String> entry = TypeInference.inferType(requestURI, requestMethod);
                ((ObjectNode) jsonNode).put(entry.getKey(), entry.getValue());
            }
    
            body = jsonNode.toString();
        }
    
        private String readBody(HttpServletRequest request) {
            StringBuilder stringBuilder = new StringBuilder();
            BufferedReader bufferedReader = null;
            try {
                InputStream inputStream = request.getInputStream();
                if (inputStream != null) {
                    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    char[] charBuffer = new char[128];
                    int bytesRead = -1;
                    while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                        stringBuilder.append(charBuffer, 0, bytesRead);
                    }
    
                }
            } catch (IOException ex) {
                throw new ApiServiceException("Error while reading payload body", ex);
            } finally {
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    } catch (IOException ex) {
                        throw new ApiServiceException("Error while closing the buffered reader", ex);
                    }
                }
            }
            return stringBuilder.toString();
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
            return new ServletInputStream() {
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener listener) {
    
                }
            };
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
    }
     
    

    然后添加一个新的过滤器

    @Component
    public class TypeInferenceRequestFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            TypeInferenceRequestWrapper wrappedRequest = new TypeInferenceRequestWrapper((HttpServletRequest) request);
            chain.doFilter(wrappedRequest, response);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

    以及将随着需求而变化的实际逻辑的类

    public class TypeInference {
        public static Map.Entry<String, String> inferType(String requestURI, String requestMethod) {
            if (requestURI.matches("\\/api\\/v1\\/cars\\/[0-9]+")
                    && requestMethod.equals(HttpMethod.PATCH.toString())) {
        
                // Get transmission type from database as this is already saved in db
                long carId = getCarId(requestURI);  
                TransmissionType type = getTransmissionTypeForCar(carId)
                return new AbstractMap.SimpleEntry<>("transmissionType", type.toString());
            }
    
            return new AbstractMap.SimpleEntry<>("", "");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-22
      • 1970-01-01
      • 2010-11-08
      • 2020-06-17
      • 1970-01-01
      • 1970-01-01
      • 2020-01-04
      • 1970-01-01
      相关资源
      最近更新 更多