Guia do LinkRest
1. Visão geral
LinkRest é uma estrutura de código aberto para a construção de serviços da Web REST baseados em dados. Ele é baseado emJAX-RSeApache Cayenne ORM e usa um protocolo de mensagem baseado em HTTP / JSON.
Basicamente, essa estrutura visa fornecer uma maneira fácil de expor nosso armazenamento de dados na web.
Nas seções a seguir, daremos uma olhada em como podemos construir um serviço da web REST para acessar um modelo de dados usandoLinkRest.
2. Maven Dependências
Para começar a trabalhar com a biblioteca, primeiro precisamos adicionar a dependêncialink-rest:
com.nhl.link.rest
link-rest
2.9
Isso também traz o artefatocayenne-server.
Além disso, usaremosJersey como a implementaçãoJAX-RS, então precisamos adicionar a dependênciajersey-container-servlet, bem comojersey-media-moxy para serializar as respostas JSON:
org.glassfish.jersey.containers
jersey-container-servlet
2.25.1
org.glassfish.jersey.media
jersey-media-moxy
2.25.1
Para o nosso exemplo, trabalharemos com um banco de dados em memóriaH2, pois é mais fácil de configurar; como consequência, também adicionaremosh2:
com.h2database
h2
1.4.196
3. Cayenne Modelo de Dados
O modelo de dados com o qual trabalharemos contém uma entidadeDepartment e umaEmployee que representa uma relação um-para-muitos:
Conforme mencionado,LinkRest works with data objects generated using Apache Cayenne ORM. Trabalhar comCayenne não é o assunto principal deste artigo, portanto, para obter mais informações, consulteApache Cayenne documentation.
Salvaremos o projetoCayenne em um arquivocayenne-linkrest-project.xml.
Depois de executarcayenne-maven-plugin, isso irá gerar duas classes abstratasDepartment_ and Employee_ - que irão estender a classeCayenneDataObject, bem como duas classes concretas derivadas delas,DepartmenteEmployee.
Essas últimas classes são aquelas que podemos personalizar e usar comLinkRest.
4. LinkRest inicialização do aplicativo
Na próxima seção, iremos escrever e testar endpoints REST, então, para poder executá-los, precisamos configurar nosso tempo de execução.
Como estamos usandoJersey como a implementaçãoJAX-RS, vamos adicionar uma classe que estendeResourceConfige especifica o pacote que conterá as classes nas quais definimos os pontos de extremidade REST:
@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {
public LinkRestApplication() {
packages("com.example.linkrest.apis");
// load linkrest runtime
}
}
No mesmo construtor, precisamos construir e registrarLinkRestRuntime no contêinerJersey. Esta classe é baseada no primeiro carregamento deCayenneRuntime:
ServerRuntime cayenneRuntime = ServerRuntime.builder()
.addConfig("cayenne-linkrest-project.xml")
.build();
LinkRestRuntime lrRuntime = LinkRestBuilder.build(cayenneRuntime);
super.register(lrRuntime);
Finalmente, precisamos adicionar a classe aoweb.xml:
linkrest
org.glassfish.jersey.servlet.ServletContainer
javax.ws.rs.Application
com.example.LinkRestApplication
1
linkrest
/*
5. Recursos REST
Agora que temos nossas classes de modelo, podemos começar a escrever recursos REST.
OREST endpoints are created using standard JAX-RS annotations, while the response is built using the LinkRest class.
Nosso exemplo consistirá em escrever uma série de endpoints CRUD que acessam a URL/department usando diferentes métodos HTTP.
Primeiro, vamos criar a classeDepartmentResource, que é mapeada para/department:
@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {
@Context
private Configuration config;
// ...
}
A classeLinkRest precisa de uma instância da classeJAX-RS Configuration, que é injetada usando a anotaçãoContext, também fornecida porJAX-RS.
Em seguida, vamos continuar escrevendo cada um dos endpoints que acessam objetosDepartment.
5.1. Criação de entidades usando POST
Para criar uma entidade, a classeLinkRest fornece o métodocreate() que retorna um objetoUpdateBuilder:
@POST
public SimpleResponse create(String data) {
return LinkRest.create(Department.class, config).sync(data);
}
O parâmetro de dados pode ser um único objeto JSON representando umDepartment ou uma matriz de objetos. Este parâmetro é enviado paraUpdateBuilder usando o métodosync() para criar um ou mais objetos e inserir os registros no banco de dados, após o que o método retorna umSimpleResponse.
A biblioteca define 3 formatos adicionais para respostas:
-
DataResponse<T> - uma resposta que representa uma coleção deT
-
MetadataResponse<T> - contém informações de metadados sobre o tipo
-
SimpleResponse - um objeto que contém dois atributossuccessemessage
A seguir, vamos usarcurl para adicionar um registroDepartment ao banco de dados:
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"IT"}" http://localhost:8080/linkrest/department
Como resultado, o comando retorna o status201 Createde um atributosuccess:
{"success":true}
Também podemos criar vários objetos enviando uma matriz JSON:
curl -i -X POST -H "Content-Type:application/json"
-d "[{"name":"HR"},{"name":"Marketing"}]"
http://localhost:8080/linkrest/department
5.2. Leitura de entidades usando GET
O principal método para consultar objetos é o métodoselect() da classeLinkRest. Isso retorna um objetoSelectBuilder que podemos usar para encadear métodos adicionais de pesquisa ou filtragem.
Vamos criar um endpoint na classeDepartmentResource que retorna todos os objetosDepartment no banco de dados:
@GET
public DataResponse getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config).uri(uriInfo).get();
}
A chamadauri() define as informações de solicitação paraSelectBuilder, enquanto get () retorna uma coleção deDepartments agrupada como um objetoDataResponse<Department>.
Vamos dar uma olhada nos departamentos que adicionamos antes de usar este endpoint:
curl -i -X GET http://localhost:8080/linkrest/department
A resposta assume a forma de um objeto JSON com uma matrizdata e uma propriedadetotal:
{"data":[
{"id":200,"name":"IT"},
{"id":201,"name":"Marketing"},
{"id":202,"name":"HR"}
],
"total":3}
Como alternativa, para recuperar uma coleção de objetos, também podemos recuperar um único objeto usandogetOne() em vez deget().
Vamos adicionar um endpoint mapeado para/department/{departmentId} que retorna um objeto com um determinado id. Para isso, filtraremos os registros usando o métodobyId():
@GET
@Path("{id}")
public DataResponse getOne(@PathParam("id") int id,
@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config)
.byId(id).uri(uriInfo).getOne();
}
Em seguida, podemos enviar uma solicitação GET para este URL:
curl -i -X GET http://localhost:8080/linkrest/department/200
O resultado é uma matrizdata com um elemento:
{"data":[{"id":200,"name":"IT"}],"total":1}
5.3. Atualizando Entidades Usando PUT
Para atualizar os registros, podemos usar o métodoupdate() oucreateOrUpdate(). O último atualizará os registros, se existirem, ou os criará, se não existirem:
@PUT
public SimpleResponse createOrUpdate(String data) {
return LinkRest.createOrUpdate(Department.class, config).sync(data);
}
Da mesma forma que nas seções anteriores, o argumentodata pode ser um único objeto ou uma matriz de objetos.
Vamos atualizar um dos departamentos adicionados anteriormente:
curl -i -X PUT -H "Content-Type:application/json"
-d "{"id":202,"name":"Human Resources"}"
http://localhost:8080/linkrest/department
Isso retorna um objeto JSON com uma mensagem de sucesso ou erro. Posteriormente, podemos verificar se o nome do departamento com ID 202 foi alterado:
curl -i -X GET http://localhost:8080/linkrest/department/202
Com certeza, este comando retorna o objeto com o novo nome:
{"data":[
{"id":202,"name":"Human Resources"}
],
"total":1}
5.4. Removendo entidades usandoDELETE
E, para remover um objeto, podemos chamar o métododelete() que cria umDeleteBuilder, em seguida, especificar a chave primária do objeto que queremos excluir usando o métodoid():
@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
return LinkRest.delete(Department.class, config).id(id).delete();
}
Então, podemos chamar este endpoint usandocurl:
curl -i -X DELETE http://localhost:8080/linkrest/department/202
5.5. Trabalhando com Relações Entre Entidades
LinkRest também contém métodos que facilitam o trabalho com relacionamentos entre objetos.
ComoDepartment tem uma relação um-para-muitos comEmployee, vamos adicionar um endpoint/department/{departmentId}/employees que acessa uma classeEmployeeSubResource:
@Path("{id}/employees")
public EmployeeSubResource getEmployees(
@PathParam("id") int id, @Context UriInfo uriInfo) {
return new EmployeeSubResource(id);
}
A classeEmployeeSubResource corresponde a um departamento, portanto, terá um construtor que define um id de departamento, bem como a instânciaConfiguration:
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeSubResource {
private Configuration config;
private int departmentId;
public EmployeeSubResource(int departmentId, Configuration configuration) {
this.departmentId = departmentId;
this.config = config;
}
public EmployeeSubResource() {
}
}
Observe que um construtor padrão é necessário para que o objeto seja serializado como um objeto JSON.
A seguir, vamos definir um endpoint que recupere todos os funcionários de um departamento:
@GET
public DataResponse getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Employee.class, config)
.toManyParent(Department.class, departmentId, Department.EMPLOYEES)
.uri(uriInfo).get();
}
Neste exemplo, usamos o métodotoManyParent() doSelectBuilder para consultar apenas os objetos com um determinado pai.
Os terminais dos métodos POST, PUT, DELETE podem ser criados de maneira semelhante.
Para adicionar funcionários a um departamento, podemos chamar o endpointdepartments/{departmentId}/employees com o método POST:
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees
Então, vamos enviar uma solicitação GET para ver os funcionários do departamento:
curl -i -X GET "http://localhost:8080/linkrest/department/200/employees
- Isso retorna um objeto JSON com uma matriz de dados
{"data":[{"id":200,"name":"John"}],"total":1}
6. Personalização da resposta com parâmetros de solicitação
LinkRest fornece uma maneira fácil de personalizar a resposta adicionando parâmetros específicos à solicitação. Eles podem ser usados para filtrar, classificar, paginar ou restringir o conjunto de atributos do conjunto de resultados.
6.1. Filtragem
Podemos filtrar os resultados com base nos valores dos atributos usando o parâmetrocayenneExp. Como o nome sugere, segue o formato deCayenne expressions.
Vamos enviar uma solicitação que retorna apenas departamentos com o nome “TI”:
curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'
6.2. Ordenação
Os parâmetros a serem adicionados para classificar um conjunto de resultados sãosortedir. O primeiro deles especifica o atributo a ser classificado e o segundo a direção da classificação.
Vamos ver todos os departamentos classificados por nome:
curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"
6.3. Paginação
A biblioteca suporta paginação adicionando os parâmetrosstartelimit:
curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=2
6.4. Selecionando Atributos
Usando os parâmetrosincludeeexclude, podemos controlar quais atributos ou relacionamentos são retornados no resultado.
Por exemplo, vamos enviar uma solicitação que exibe apenas os nomes dos departamentos:
curl -i -X GET "http://localhost:8080/linkrest/department?include=name
Para mostrar os nomes e também os funcionários de um departamento apenas com seus nomes, podemos usar o atributoinclude duas vezes:
curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name
7. Conclusão
No artigo, mostramos como podemos expor rapidamente um modelo de dados por meio de endpoints REST usando a estruturaLinkRest.
O código-fonte completo dos exemplos pode ser encontradoover on GitHub.