Requests for Rest API

Asked

Viewed 1,876 times

20

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

Vehicle has as attribute an agency list and a contact list, paths are generated:

daniela.morais@tusk:~$ 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 an object Vehicle but I can only get through nome and tipo

daniela.morais@tusk:~$ 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:

daniela.morais@tusk:~$ 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 to /api/vehicle/{id} however I don’t know how to insert a list of agencies and contacts.
My question is: by default REST, all CRUD referring to 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? Therefore, how do I create my Agency and Contact list?

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

daniela.morais@tusk:~$ curl http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias
  {
    "_links" : {
      "self" : {
        "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias"
      }
    },
    "_embedded" : {
      "agencias" : [ ]
    }
  }

daniela.morais@tusk:~$ 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

daniela.morais@tusk:~$ curl http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias
  {
    "_links" : {
      "self" : {
        "href" : "http://localhost:8181/api/veiculos/55a50d42ccf2bc55501419d6/agencias"
      }
    },
    "_embedded" : {
      "agencias" : [ ]
    }
  }

My entities are these (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 annotation @RestResource(exported = false) attributes that are listed in Vehicle, but:
I. this data is not "relational"
II. If I set up an agency in api/agencies and then get this ID to update the agencies on api/vehicles he does not recognize the reference.
How do I fix this?

daniela.morais@tusk:~$ 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"}daniela.morais@tusk:~$ ^C
daniela.morais@tusk:~$ 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"
    }
  }
}
daniela.morais@tusk:~$ 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
  }
}

1 answer

20


It was very difficult for me to solve my problem because I had no knowledge needed to deal with Spring Data Rest, then I will explain in detail:

Spring Data Rest

Spring Data Rest is used to facilitate the development of API’s Restful and allow the focus to be only on developing the business logic as it avoids us from repetitions 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 methods, POST, GET, PATCH etc. for the respective methods. All this occurs without the need to practically develop none 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;

}

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

daniela@daniela-tars:~/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 the best ripening point of an API as it enables easier navigation between API features. When a request such as POST, GET and PUT is sent, this information is returned, along with JSON information about what else is possible to do.
inserir a descrição da imagem aqui

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

daniela@daniela-tars:~/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
  }
}daniela@daniela-tars:~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

The moment I create a vehicle, I cannot send an Array of Agencies/Vehicles because they are attributes of reference which have been declared with the annotation @DBRef and these have their own Sources (/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 and contact source in the contact source
2. Creating a vehicle and relating to the agencies and contacts created

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

Relational banks

For relational banks only, the relationship has to be verified and how it will associate.

Non-relational banks

No need 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 a 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, simply pass the agency URI array:

POST /vehicles

daniela.morais@tusk:~$ 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 the association

daniela.morais@tusk:~$ 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 example simpler association with mongoDB.
It is also possible to associate by sending a PUT/POST to the Uri-list, independent bank:
I. Posting to @Onetomany sub-Resource Association in Spring Data REST
II. Spring Data REST: Silent Failure when Adding Entity Relationship
III. Exposing Spring Data repositories over REST

Although simple the solution, I leave here Sensedia links about API’s that helped me understand my problem
I. Webinar: The Fundamentals of API Security
II. Webinar Restful API Design

Browser other questions tagged

You are not signed in. Login or sign up in order to post.