Reputation: 185
I am developing a set of rest resources over a database and exposing the core CRUD functionality using Spring Data Rest to directly interact with the Repositories.
In my simplified sample I have Users:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long id;
public String name;
@OneToMany(mappedBy = "user")
public Collection<Project> projects;
}
and users Own Projects:
@Entity
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long id;
public String name;
public String oneOfManyComplexDerivedProperties;
@ManyToOne
public User user;
}
Directly interacting with the repositories is fine, so for creating Users (other other simple entities), the problem comes with creating Projects. Projects have a large number of server derived fields based on the users form input, so I wrote a custom controller to generate them and persist the result. In order to persist the result, I need to associate the Project with it's owning User. I want my client to be able to use the user Link for this, just as when creating a new entity by going direct to the repository (going direct to the repository just works):
@RepositoryRestController
public class CustomProjectController {
@Autowired
ProjectRepo projectRepo;
@RequestMapping(value = "/createProject", method = RequestMethod.POST)
public HttpEntity<Project> createProject(@RequestParam User userResource,
@RequestParam String formField1, // actually an uploaded file that gets processed, but i want simple for example purposes
@RequestParam String formfield2)
{
Project project = new Project();
/*
Actually a large amount of complex business logic to derive properties from users form fields, some of these results are binary.
*/
String result = "result";
project.oneOfManyComplexDerivedProperties = result;
project.user = userResource;
projectRepo.save(project);
// aware that this is more complex than I've written.
return ResponseEntity.ok(project);
}
}
I get:
{
"timestamp": 1510588643801,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
"message": "Failed to convert value of type 'java.lang.String' to required type 'com.badger.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'http://localhost:9999/api/users/1'; nested exception is java.lang.NumberFormatException: For input string: \"http://localhost:9999/api/users/1\"",
"path": "/api/createProject"
}
If I change userResource to type Resource
then I get a different error:
"Failed to convert value of type 'java.lang.String' to required type 'org.springframework.hateoas.Resource'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.springframework.hateoas.Resource': no matching editors or conversion strategy found"
I can't find any reference to using repository URI's in custom controllers in the docs, the closest I found was Resolving entity URI in custom controller (Spring HATEOAS) but the API's have changed since that was written and I have not been able to get it to work.
Upvotes: 1
Views: 672
Reputation: 23226
I would suggest that what you should really be doing is:
http://localhost:9999/api/users/1/projects?formField1=data1&formField2=Otherdata
By enable Spring Data's web support you can have the path variable automatically bound to the entity instance.
@RequestMapping(value = "users/{id}/projects", method = RequestMethod.POST)
public HttpEntity<Project> createProject(
@PathVariable("id") User user,
@RequestParam String formField1,
@RequestParam String formfield2)
{
}
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web
Upvotes: 1
Reputation: 1774
What User userResource requires is a User object what you passed in was a String http://localhost:9999/api/users/1
So I would recommend you changing your request to a POST
request and passing your User Object as the body of that request.
The body of the request will look like in JSON format:
{
id: 1,
name: "myName",
projects: []
}
so you then remove userResource=http://localhost:9999/api/users/1
from your URL.
Finally change @RequestParam User userResource
to @RequestBody User userResource
EDIT
since you have the user Id in your request you can make a call within the controller to find the user by the Id. So you can change the user object from @RequestParam User userResource
to @RequestParam long userId
then make a call to find the user something like findUserById(userId)
inside your method
so you url will look like http://localhost:9999/api/createProject?userId=1&formField1=data1&formField2=Otherdata
Upvotes: 0
Reputation: 1279
If you want to stick with the url params you could change the @RequestParam User userResource
to @RequestParam String userId
And then in your createProject code have something along the lines of
User user = userRepo.findOne(userId);
project.user = user;
....
projectRepo.save(project);
If it was me I'd define a CreateProjectRequest
object which you pass in, e.g.
{
"userId" : "1",
"formField1" : "whatever",
"formField2" : "whatever"
}
And change your createProject to
createProject(@RequestBody CreateProjectRequest createProjectRequest)
...
project.setField1(createProjectRequest.getFormField1());
...
User user = userRepo.findOne(createProjectRequest.getUserId());
project.user = user;
....
projectRepo.save(project);
Upvotes: 0