Requests for API Rest

18

The application uses Spring Rest in which the paths of a CRUD are generated automatically for each entity, I have the Vehicle, Contact and Agency entities and each one with its respective repository.

Vehicle has as an attribute an Agency list and a Contact list, the paths are generated:

[email protected]:~$ curl http://localhost:8181/api
{
  "_links" : {
    "agencias" : {
      "href" : "http://localhost:8181/api/agencias{?page,size,sort}",
      "templated" : true
    },
    "veiculos" : {
      "href" : "http://localhost:8181/api/veiculos{?page,size,sort}",
      "templated" : true
    },
    "contatos" : {
      "href" : "http://localhost:8181/api/contatos{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:8181/api/alps"
    }
  }
}

I created a Vehicle object but I can only pass nome and tipo

[email protected]:~$ curl http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6
{
  "nome" : "veiculo",
  "tipo" : "tipo",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6"
    },
    "contatos" : {
      "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/contatos"
    },
    "agencias" : {
      "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias"
    }
  }
}

This occurs when I try to send an Array:

[email protected]:~$ curl -X POST -H "Content-Type:application/json" -d '{"nome": "teste", "tipo": "tipo", "agencias": [{"nome": "agencia"}]}' http://localhost:8181/api/veiculos
{"cause":{"cause":{"cause":null,"message":"Template must not be null or empty!"},"message":"Template must not be null or empty! (through reference chain: oknok.entities.Veiculo[\"agencias\"]->java.util.ArrayList[0])"},"message":"Could not read JSON: Template must not be null or empty! (through reference chain: oknok.entities.Veiculo[\"agencias\"]->java.util.ArrayList[0]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: oknok.entities.Veiculo[\"agencias\"]->java.util.ArrayList[0])"}

I can execute all requests for / api / vehicle / {id} but I do not know how to insert a list of agencies and contacts. My question is: by REST , all CRUD for contacts and agencies must be done in this path , api / vehicles / {id} / contacts and api / vehicles / {id} / agencies , which was generated because it will redirect all requests to their respective repositories, correct? So how do I create my list of Agencies and Contacts?

I can not give PUT, but only POST and GET in these paths, I tried to send a POST with a JSON that has an Array, but when I give GET it is not displayed

[email protected]:~$ curl http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias
  {
    "_links" : {
      "self" : {
        "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias"
      }
    },
    "_embedded" : {
      "agencias" : [ ]
    }
  }

[email protected]:~$ curl-i -X PUT -H "Content-Type: application/json" -d '{"agencias": [{"nome": "um"}]}' http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias
  HTTP/1.1 204 No Content
  Server: Apache-Coyote/1.1
  X-Content-Type-Options: nosniff
  X-XSS-Protection: 1; mode=block
  Cache-Control: no-cache, no-store, max-age=0, must-revalidate
  Pragma: no-cache
  Expires: 0
  X-Frame-Options: DENY
  Date: Tue, 14 Jul 2015 14:34:11 GMT

[email protected]:~$ curl http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias
  {
    "_links" : {
      "self" : {
        "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias"
      }
    },
    "_embedded" : {
      "agencias" : [ ]
    }
  }

My entities are these (I hid the getters and setters) with their repositories:

Veiculo

@Document
public class Veiculo {

    @Id
    private String id;

    @Indexed(unique = true)
    private String nome;

    private String tipo;

    @DBRef
    List<Contato> contatos;

    @DBRef
    List<Agencia> agencias;

}

Veiculo Repository

@RepositoryRestResource(collectionResourceRel = "veiculos", path = "veiculos")
public interface VeiculoRepository extends MongoRepository<Veiculo, String> {
    Veiculo save(Veiculo veiculo);

    List<Veiculo> findAll();
}

Agencia

@Document
public class Agencia {

    @Id
    String id;
    String nome;

    @CreatedBy
    String createdBy;

    @LastModifiedBy
    String lastModifiedBy;

    @CreatedDate
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    Date createdAt;

    @LastModifiedDate
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    Date lastModified;

}

Agencia Repository

@RepositoryRestResource(collectionResourceRel = "agencias", path = "agencias")
public interface AgenciaRepository extends MongoRepository<Agencia, String> {

    @PreAuthorize("hasRole('ADMIN')")
    Agencia save(Agencia t);
    List<Agencia> findAll();

}

Contato

@Document
public class Contato {

    @Id
    private String id;

    String nome;

    List<Info> dados;

    @DBRef
    Agencia agencia;

}

Contato Repository

@RepositoryRestResource(collectionResourceRel = "contatos", path = "contatos")
public interface ContatoRepository extends MongoRepository<Contato, String> {

    List<Contato> findByNome(@Param("nome") String nome);
    List<Contato> findByAgencia(@Param("agencia") String agencia);

}

** UPDATE I was able to send an Array by adding the @RestResource(exported = false) annotation to the attributes that are lists in Vehicle, however:
I. this data is not "relational"
II. If I create an agency in api / agencies and then pick up this ID to update agencies on api / vehicles it does not recognize the reference.
How do I fix this?

[email protected]:~$ curl -X POST -H "Content-Type:application/json" -d '{"nome": "teste", "tipo": "tipo", "agencias": [{"nome": "agencia"}]}' http://localhost:8181/api/veiculos
{"timestamp":1437133673823,"status":500,"error":"Internal Server Error","exception":"org.springframework.data.mapping.model.MappingException","message":"Cannot create a reference to an object with a NULL id.","path":"/api/veiculos"}[email protected]:~$ ^C
[email protected]:~$ curl -X POST -H "Content-Type:application/json" -d '{"nome": "teste", "tipo": "tipo", "agencias": [{"nome": "agencia", "id": "1"}]}' http://localhost:8181/api/veiculos
{
  "nome" : "teste",
  "tipo" : "tipo",
  "contatos" : null,
  "agencias" : [ {
    "nome" : "agencia",
    "createdBy" : null,
    "lastModifiedBy" : null,
    "createdAt" : null,
    "lastModified" : null
  } ],
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/veiculos/55a8eb8544ae13951d3f2b6f"
    },
    "agencia" : {
      "href" : "http://localhost:8181/api/veiculos/55a8eb8544ae13951d3f2b6f/agencia"
    }
  }
}
[email protected]:~$ curl http://localhost:8181/api/agencias
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/agencias"
    }
  },
  "_embedded" : {
    "agencias" : [ ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}
    
asked by anonymous 14.07.2015 / 17:20

1 answer

19

It was very difficult for me to be able to solve my problem because I did not have the necessary knowledge to handle Spring Data Rest, so I'll explain in detail:

Spring Data Rest

Spring Data Rest is used to facilitate the development of API's RestFul / a> and allow the focus to be only on the development of business logic because it avoids repetition when developing an API, only with the creation of entities and an interface of its respective repository it maps the paths and directs the PUT, POST , GET, PATCH, etc. for the respective methods. All this occurs without the need to practically develop any code.

Example

Repository

@RepositoryRestResource(collectionResourceRel = "contatos", path = "contatos")
public interface ContatoRepository extends MongoRepository<Contato, String> {

    List<Contato> findByNome(@Param("nome") String nome);
    List<Contato> findByAgencia(@Param("agencia") String agencia);

}

Entity without getters and setters

@Document
public class Contato {

    @Id
    private String id;
    String nome;
    List<Info> dados;
    @DBRef
    Agencia agencia;

}

Only with these classes above, Spring already allows me to make the requests and directs to the method responsible:

[email protected]:~/microservices$ curl -i -X OPTIONS localhost:8181/api/contatos
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Sat, 18 Jul 2015 19:43:48 GMT

HATEOAS

HATEOAS or Hypermedia as the Engine of Application State is considered to be the best maturation point of an API because it enables easier navigation between API resources. When a request such as POST, GET, and PUT is sent, which return information, along with JSON, information about what else is possible is returned.

For example, along with the return of the listing of all vehicles I get the vehicle's URI. That is, I can execute a request for that URI. If I send a GET to a specific vehicle, I get the Vehicle URI contacts and agencies, which I can also access, and so on:

[email protected]:~/microservices/microservices$ curl localhost:8181/api/veiculos
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/veiculos"
    }
  },
  "_embedded" : {
    "veiculos" : [ {
      "nome" : "veiculo",
      "tipo" : "tipo",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504"
        },
        "contatos" : {
          "href" : "http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504/contatos"
        },
        "agencias" : {
          "href" : "http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504/agencias"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}[email protected]:~644aeceb9f439c504croservices$ curl http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504 
{
  "nome" : "veiculo",
  "tipo" : "tipo",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504"
    },
    "contatos" : {
      "href" : "http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504/contatos"
    },
    "agencias" : {
      "href" : "http://localhost:8181/api/veiculos/55aaa7d644aeceb9f439c504/agencias"
    }
  }
}  

URI Associations

When I create a vehicle, I can not send an Agencies / Vehicles Array because they are reference attributes that were declared with the @DBRef annotation and they have their own resources ( / api / contacts and / api / agencies ), which would make no sense at all. What it takes to solve my problem is:
1. Create an Agency in the Agency Resource and Contact in the Contact Resource
2. Create a vehicle and relate to the created agencies and contacts.

Attention
I got a InvalidDataAccessResourceUsageException after creating the association because in my case the default is the bank to be executed in memory, after the associations I simply could not access the paths. To run with the bank, just run mvn spring-boot:run in the directory.

Relational Banks

Relational banks only have to verify the relationship and how you will associate.

Non-Relational Banks

It is not necessary to specify how the relationship will be, as it says in the API reference:

  

There's no need to use something like @OneToMany because the mapping   framework sees that you're wanting to one-to-many relationship because   there is a List of objects. When the object is stored in MongoDB,   there will be a list of DBRefs rather than the Account objects   themselves.

To associate, just pass in the array the agency URIs:

POST / vehicles

[email protected]:~$ curl -i -X POST -H "Content-Type: application/json" -d '"nome": "Veiculo", "tipo": "tipo", "agencias": ["http://localhost:8181/api/agencias/55ae37edccf2070af2e5ab1d", "http://localhost:8181/api/agencias/55ae37e8ccf2070af2e5ab1c"]}' localhost:8181/api/veiculos
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Location: http://localhost:8181/api/veiculos/55ae3813ccf2070af2e5ab1e
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 21 Jul 2015 12:16:19 GMT

{
  "nome" : "Veiculo",
  "tipo" : "tipo",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/veiculos/55ae3813ccf2070af2e5ab1e"
    },
    "contatos" : {
      "href" : "http://localhost:8181/api/veiculos/55ae3813ccf2070af2e5ab1e/contatos"
    },
    "agencias" : {
      "href" : "http://localhost:8181/api/veiculos/55ae3813ccf2070af2e5ab1e/agencias"
    }
  }
}

GET to check association

[email protected]:~$ curl -i -X GET http://localhost:8181/api/veiculos/55ae383ccf2070af2e5ab1e/agencias
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 21 Jul 2015 12:16:45 GMT

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8181/api/veiculos/55ae3813ccf2070af2e5ab1e/agencias"
    }
  },
  "_embedded" : {
    "agencias" : [ {
      "nome" : "Agencia dois",
      "createdBy" : "anonymousUser",
      "lastModifiedBy" : "anonymousUser",
      "createdAt" : "2015-07-21T12:15:41.286+0000",
      "lastModified" : "2015-07-21T12:15:41.286+0000",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8181/api/agencias/55ae37edccf2070af2e5ab1d"
        }
      }
    }, {
      "nome" : "Agencia um",
      "createdBy" : "anonymousUser",
      "lastModifiedBy" : "anonymousUser",
      "createdAt" : "2015-07-21T12:15:36.369+0000",
      "lastModified" : "2015-07-21T12:15:36.369+0000",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8181/api/agencias/55ae37e8ccf2070af2e5ab1c"
        }
      }
    } ]
  }

A simpler example of association with mongoDB

example . Home You can also associate by sending a PUT / POST with the uri-list, stand-alone of the database:
I. POSTing the @OneToMany sub-resource association in Spring Data REST
II. Spring Data REST: Silent failure when adding entity relationship Home III. Exposing Spring Data repositories over REST

Although the solution is simple, I leave here links from Sensedia about API's that helped me to understand my problem
I. Webinar: The Fundamentals of APIs Security
II. Webinar Designing RESTful APIs

    
18.07.2015 / 23:44