Spring Data REST: projection representation of single resource

前端 未结 2 1872
孤街浪徒
孤街浪徒 2020-12-21 16:16

I have a simple UserRepository which exposed using Spring Data REST. Here is the User entity class:

@Document(collecti         


        
相关标签:
2条回答
  • 2020-12-21 16:38

    I was able to create a ResourceProcessor class which applies projections on any resource as suggested in DATAREST-428. It works the following way: if projection parameter is specified in URL - the specified projection will be applied, if not - projection with name default will be returned, applied first found projection will be applied. Also, I had to add custom ProjectingResource which ignores the links, otherwise there are two _links keys in the returning JSON.

    /**
     * Projecting resource used for {@link ProjectingProcessor}. Does not include empty links in JSON, otherwise two 
     * _links keys are present in returning JSON.
     *
     * @param <T>
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    class ProjectingResource<T> extends Resource<T> {
    
        ProjectingResource(final T content) {
            super(content);
        }
    }
    
    /**
     * Resource processor for all resources which applies projection for single resource. By default, projections
     * are not
     * applied when working with single resource, e.g. http://127.0.0.1:8080/users/580793f642d54436e921f6ca. See
     * related issue <a href="https://jira.spring.io/browse/DATAREST-428">DATAREST-428</a>
     */
    @Component
    public class ProjectingProcessor implements ResourceProcessor<Resource<Object>> {
    
        private static final String PROJECTION_PARAMETER = "projection";
    
        private final ProjectionFactory projectionFactory;
    
        private final RepositoryRestConfiguration repositoryRestConfiguration;
    
        private final HttpServletRequest request;
    
        public ProjectingProcessor(@Autowired final RepositoryRestConfiguration repositoryRestConfiguration,
                                   @Autowired final ProjectionFactory projectionFactory,
                                   @Autowired final HttpServletRequest request) {
            this.repositoryRestConfiguration = repositoryRestConfiguration;
            this.projectionFactory = projectionFactory;
            this.request = request;
        }
    
        @Override
        public Resource<Object> process(final Resource<Object> resource) {
            if (AopUtils.isAopProxy(resource.getContent())) {
                return resource;
            }
    
            final Optional<Class<?>> projectionType = findProjectionType(resource.getContent());
            if (projectionType.isPresent()) {
                final Object projection = projectionFactory.createProjection(projectionType.get(), resource
                        .getContent());
                return new ProjectingResource<>(projection);
            }
    
            return resource;
        }
    
        private Optional<Class<?>> findProjectionType(final Object content) {
            final String projectionParameter = request.getParameter(PROJECTION_PARAMETER);
            final Map<String, Class<?>> projectionsForType = repositoryRestConfiguration.getProjectionConfiguration()
                    .getProjectionsFor(content.getClass());
    
            if (!projectionsForType.isEmpty()) {
                if (!StringUtils.isEmpty(projectionParameter)) {
                    // projection parameter specified
                    final Class<?> projectionClass = projectionsForType.get(projectionParameter);
                    if (projectionClass != null) {
                        return Optional.of(projectionClass);
                    }
                } else if (projectionsForType.containsKey(ProjectionName.DEFAULT)) {
                    // default projection exists
                    return Optional.of(projectionsForType.get(ProjectionName.DEFAULT));
                }
    
                // no projection parameter specified
                return Optional.of(projectionsForType.values().iterator().next());
            }
    
            return Optional.empty();
        }
    }
    
    0 讨论(0)
  • 2020-12-21 16:42

    I was looking at something similar recently and ended up going round in circles when trying to approach it from the Spring Data /Jackson side of things.

    An alternative, and very simple solution, then is to approach it from a different angle and ensure the Projection parameter in the HTTP request is always present. This can be done by using a Servlet Filter to modify the parameters of the incoming request.

    This would look something like the below:

    public class ProjectionResolverFilter extends GenericFilterBean {
    
        private static final String REQUEST_PARAM_PROJECTION_KEY = "projection";
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
                throws IOException, ServletException {
    
            HttpServletRequest request = (HttpServletRequest) req;
    
            if (shouldApply(request)) {
                chain.doFilter(new ResourceRequestWrapper(request), res);
            } else {
                chain.doFilter(req, res);
            }
        }
    
        /**
         * 
         * @param request
         * @return True if this filter should be applied for this request, otherwise
         *         false.
         */
        protected boolean shouldApply(HttpServletRequest request) {
            return request.getServletPath().matches("some-path");
        }
    
        /**
         * HttpServletRequestWrapper implementation which allows us to wrap and
         * modify the incoming request.
         *
         */
        public class ResourceRequestWrapper extends HttpServletRequestWrapper {
    
            public ResourceRequestWrapper(HttpServletRequest request) {
                super(request);
            }
    
            @Override
            public String getParameter(final String name) {
                if (name.equals(REQUEST_PARAM_PROJECTION_KEY)) {
                    return "nameOfDefaultProjection";
                }
    
                return super.getParameter(name);
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题