Guia do LinkRest

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:

image

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.