Попытка динамического управления сериализацией ответа REST на основе параметров HTTP в приложении весенней загрузки - PullRequest
0 голосов
/ 01 февраля 2019

У нас есть загрузочное приложение Spring, которое предоставляет несколько интерфейсов REST API для Angular UI.Мы сталкиваемся с проблемой производительности, показывая списки вещей.Angular отправляет запрос GET REST на сервер и получает поверхностный список элементов.Когда пользовательский интерфейс отображает список, для каждой строки необходимо загрузить множество деталей, поэтому он отправляет несколько запросов на строку.Чтобы показать первую страницу с 50 элементами, она отправляет несколько сотен запросов.Чтобы оптимизировать его, мы должны иметь возможность «обогатить» первоначальный запрос списка, чтобы включить всю эту информацию.Какие дочерние элементы должны контролироваться параметрами HTTP исходного запроса, чтобы он содержал всю информацию, необходимую для отображения списка.

Что-то вроде: ?children=contacts&children=document&children=document.notes

Я экспериментировал с разнымиОсобенности Джексона.Представления, перспективы, JsonIgnore и другие статические способы здесь не помогут.Поэтому я посмотрел на фильтры и пользовательские сериализации.Мне удалось заставить его работать несколько, но ...

Я пытался создать фильтры и сериализаторы для каждого класса (Case, Document и т. Д.), Однако, сериализация никогда не идет "глубже", чем корневой объект.

Я относительно новичок в Spring-boot и Джексоне.У меня заканчиваются идеи.По сути, мой код до сих пор выглядит следующим образом (я извиняюсь за столько подробностей, но это как наименьшее извлечение, которое я мог бы сделать без потери важных частей)

класс модели:

@JsonFilter("CaseFilter")
@Entity
public class Case implements Serializable, Cloneable {

    @Id
    private Integer id;

    @OneToOne(mappedBy = "Case", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
    private Document document;

    // ...
}

@JsonFilter("DocumentFilter")
@Entity
public class Document implements Serializable, Cloneable {

    @Id
    private Integer id;

    @JsonBackReference
    @OneToOne(optional = false, fetch = FetchType.EAGER)
    private Case case;

    // ...
}

Затем я создал собственный сериализатор для каждой модели


public class ModelSerializationConfig {
    private String filterName;
    private String parameterPrefix;
    private Class modelClass;

    public ModelSerializationConfig(String filterName, String parameterPrefix, Class modelClass) {
        this.filterName = filterName;
        this.parameterPrefix = parameterPrefix;
        this.modelClass = modelClass;
    }

    public String getFilterName() {
        return filterName;
    }

    public String getParameterPrefix() {
        return parameterPrefix;
    }

    public Class getModelClass() {
        return modelClass;
    }
}
@JsonComponent
public class CaseSerializer extends JsonSerializer<Case> {

    public static ModelSerializationConfig CONFIG =
            new ModelSerializationConfig("CaseFilter", "case.", Case.class);

    @Autowired
    private FilteringSerializerService serializerService;

    @Override
    public void serialize(Case case, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        serializerService.serialize(case, jsonGenerator);
    }
}

@JsonComponent
public class DocumentSerializer extends JsonSerializer<Document> {

    public static ModelSerializationConfig CONFIG =
            new ModelSerializationConfig("DocumentFilter", "case.document.", Document.class);

    @Autowired
    private FilteringSerializerService serializerService;

    @Override
    public void serialize(Document document, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        serializerService.serialize(document, jsonGenerator);
    }
}

Я расширил логику сериализации в отдельный сервис

@Component
public class FilteringSerializerService {
    private final static Logger LOGGER = LoggerFactory.getLogger(FilteringSerializerService.class);

    private final static String PARAMETER_NAME = "children";

    @Autowired
    private HttpServletRequest httpServletRequest;

    private SimpleFilterProvider filterProvider;

    String[] childrenParam = null;

    private SimpleFilterProvider getFilterProvider() {
        if(filterProvider == null) {
            filterProvider = new SimpleFilterProvider();
        }
        return filterProvider;
    }

    private void initFilter(ModelSerializationConfig config) {
        CustomFilter filter = null;
        try {
            filter = (CustomFilter)getFilterProvider().findPropertyFilter(config.getFilterName(), null);
        } catch (RuntimeException ex) {
            LOGGER.info("Need to add filter " + config.getFilterName());
            filter = new CustomFilter();
            getFilterProvider().addFilter(config.getFilterName(), filter);
        }
        filter.init(config, childrenParam);
    }

    private void initFilters() {
        initFilter(Case.CONFIG);
        initFilter(DocumentSerializer.CONFIG);
    }

    public void serialize(Object object, JsonGenerator jsonGenerator) throws IOException {
        childrenParam = httpServletRequest.getParameterValues(PARAMETER_NAME);
        LOGGER.info("service serialize called");

        LOGGER.info("filter params specified. Needs filtering {}", object.getClass().getSimpleName());
        initFilters();

        LOGGER.info("Creating mapper");
        ObjectMapper mapper = new ObjectMapper();
        mapper.setFilterProvider(getFilterProvider());
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        LOGGER.info("Mapping");
        String result = mapper
                .writerWithDefaultPrettyPrinter()
                .writeValueAsString(object);

        // Strip {} of the object
        result = result.substring(1, result.length() - 2);

        LOGGER.info("Writing");
        jsonGenerator.writeStartObject();
        jsonGenerator.writeRaw(result);
        jsonGenerator.writeEndObject();
    }
}

И, наконец, пользовательский фильтр выглядит так

public class CustomFilter implements PropertyFilter {

    public void init(ModelSerializationConfig config, String[] childrenParam) {
    // Accept config and HTTP parameters here...
    }

    protected boolean shouldInclude(Object pojo, String name) {
    // Filtering logic...
    }

    @Override
    public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception {
        if(shouldInclude(pojo, writer.getName())) {
            writer.serializeAsField(pojo, jgen, prov);
        }
    }

    @Override
    public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception {
        writer.serializeAsElement(elementValue, jgen, prov);
    }

    @Override
    public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider) throws JsonMappingException {
        writer.depositSchemaProperty(propertiesNode, provider);
    }

    @Override
    public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException {
        writer.depositSchemaProperty(objectVisitor, provider);
    }
}

На данный момент я фактически подвергаю сомнению всю эту вещь.Я подозреваю, что есть более элегантное решение этой проблемы.Я просто не могу его найти.

Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...