簡體   English   中英

Dropwizard抽象資源設計

[英]Dropwizard abstract resource design

我認為這是一個更通用的Java問題,但是我將解釋我正在嘗試做的事情,並希望有人可以為我指出正確的方法;

我試圖創建一個通用的抽象類,我所有的資源都可以從中擴展。

抽象類具有用於標准內容的基本CRUD實現。

@Produces("application/vnd.api+json")
@Consumes("application/vnd.api+json")
public abstract class AbstractResource {

    static final Logger LOGGER = LoggerFactory.getLogger(AbstractResource.class);

    AbstractRepository repository;

    AbstractResource(AbstractRepository repository) {
        this.repository = repository;
    }

    @GET
    public Response getAll(@Auth User user, @QueryParam("query") String query) {
        String result = query != null ? repository.getByQuery(query) : repository.getAll();
        return Response.status(Response.Status.OK).entity(result).build();
    }

    @GET
    @Path("/{id}")
    public Response getById(@Auth User user, @PathParam("id") String id) {
        String result = repository.getById(id);
        return Response.status(Response.Status.OK).entity(result).build();
    }

    @POST
    public Response save(@Auth User user, String payload) {
        String result = repository.save(payload);
        return Response.status(Response.Status.OK).entity(result).build();
    }

    @PATCH
    @Path("/{id}")
    public Response update(@Auth User user, @PathParam("id") String id, String payload) {
        String result = repository.update(payload);
        return Response.status(Response.Status.OK).entity(result).build();
    }

    @DELETE
    @Path("/{id}")
    public Response delete(@Auth User user, @PathParam("id") String id) {
        repository.delete(id);
        return Response.status(Response.Status.NO_CONTENT).build();
    }

}

我可以簡單地使用它

@Path("/movies")
public class MovieResource extends AbstractResource {
    public MovieResource(MovieRepository repository) {
        super(repository);
    }
}

現在,我可以訪問所有方法並根據需要進行覆蓋。

我遇到問題的地方是何時需要重載方法。 以抽象類中的第一個getAll方法為例,我只想更改Movie.class的參數

@Path("/movies")
public class MovieResource extends AbstractResource {

    public MovieResource(MovieRepository repository) {
        super(repository);
    }

    @GET
    public Response getAll(@Auth User user, @QueryParam("query") String query, @QueryParam("limit") String limit, @QueryParam("page") String page) {
        String result = repository.getPaginated(limit, page);
        return Response.status(Response.Status.OK).entity(result).build();
    }

}

因此, getAll方法在Movie.class具有一組不同的參數。 這導致澤西炸毀

[[FATAL] A resource model has ambiguous (sub-)resource method for HTTP method GET and input mime-types as defined by"@Consumes" and "@Produces" annotations at Java methods public javax.ws.rs.core.Response space.cuttlefish.domain.resources.MovieResource.getAll(space.cuttlefish.domain.model.User,java.lang.String,java.lang.String,java.lang.String) and public javax.ws.rs.core.Response space.cuttlefish.domain.resources.AbstractResource.getAll(space.cuttlefish.domain.model.User,java.lang.String) at matching regular expression /movies. These two methods produces and consumes exactly the same mime-types and therefore their invocation as a resource methods will always fail.; source='org.glassfish.jersey.server.model.RuntimeResource@6a1ef65c']

因為摘要的原始getAll方法已經具有@GET批注。

那么,我該如何解決呢?

我是否要從抽象類中刪除所有注釋,然后必須重寫並在每個資源中重新添加注釋? 這似乎很混亂並且容易出錯...這里必須有更好的解決方案嗎?

有什么東西我只是被我忽略了嗎?

希望有幫助!

我建議使用泛型。

我們已經完成了與此類似但相當復雜的版本。 一開始很難做到這一點,但是我們擁有最大的代碼可重用性(使用Java)並且易於閱讀/編寫代碼。

public abstract class AbstractResource<T extends AbstractObject, K extends AbstractObjectDto> {

    static final Logger LOGGER = LoggerFactory.getLogger(AbstractResource.class);

    AbstractRepository<T> repository;
    // We have used modelmapper library to automatically convert DTO objects to database objects. But you can come up with your own solution for that. I.E implementing conversion logic on each DTO and database classes.
    ModelMapper modelMapper = new ModelMapper(); 

    // With Java Generics, one cannot access the class type directly by simply calling 'K.class'. So you need to pass the class types explicitly as well. That is if you're using modelmapper.
    private final Class<T> objectClass;
    private final Class<K> objectDtoClass;

    AbstractResource(AbstractRepository<T> repository, Class<T> objectClass, Class<K> objectDtoClass) {
        this.repository = repository;
        this.objectClass = objectClass;
        this.objectDtoClass = objectDtoClass;
    }

    ...

    @POST
    public K save(@Auth User user, @Valid K payload) {
        T databaseObject = modelmapper.map(payload, objectClass);
        T result = repository.save(databaseObject);
        K resultDto = modelMapper.map(result, objectDtoClass);
        retun resultDto;
    }
    ...
}

然后,您需要為每個對象類型創建一個存儲庫類,該類具有必需的方法,如savegetPaginated等,從而覆蓋AbstractRepository 當然, Movie應該擴展AbstractObject類,而MovieDto應該擴展AbstractObjectDto類。

public class MovieRepository extends AbstractRepository<Movie> {
    ....
    Movie save(Movie movie) {...}
}

其余的就是這樣簡單:

@Path("/movies")
public class MovieResource extends AbstractResource<Movie, MovieDto> {

    public MovieResource(MovieRepository repository) {
        super(repository, Movie.class, MovieDto.class);
    }
}

它對您失敗的原因是在您的示例中,多個方法映射到相同的URL路徑。 但是,如果您只是重寫方法,Jersey不會抱怨。

我建議在AbstractResource中使用通用方法,在該方法中,您可以將@Context UriInfo uriInfo傳遞給您的方法,並在通用實用程序方法中解析其查詢參數,或者通過

@Path("/{segment: .*}")
@GET
@Produces("application/json")
public Response getAll(@PathParam("segment") PathSegment segment)
...

並通過通用默認方法或兩者的組合再次解析它們。

這樣,在許多情況下,您可以默認為公共端點,或者對典型的用例進行自定義預處理並委托給常見的解析方法。

如果我做對了,則在以下項目中嘗試了您想要的操作: https : //github.com/researchgate/restler (免責聲明:我是那里的貢獻者)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM