MongoDB and Spring MVC: PATCH operation? - java

I have the following simple Java Controller with the Spring Web framework:
#RestController
#RequestMapping("/rounds")
#Slf4j
public class RoundController {
private RoundService roundService;
#Autowired
public RoundController(RoundService roundService) {
this.roundService = roundService;
}
#GetMapping(
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<Round> find() {
return roundService.find();
}
#GetMapping(
path = "/{userId}",
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<Round> get(#PathVariable String userId) {
return roundService.getRoundsByUserId(userId);
}
#PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.CREATED)
public Round create(#Valid #NotNull #RequestBody Round round) {
roundService.create(round);
return round;
}
#DeleteMapping(
path = "/{id}",
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public void delete(#PathVariable String id) {
ObjectId objectId = new ObjectId(id);
roundService.delete(objectId);
}
}
When using Mongo is there a best practice for doing an update / patch for an object?
Is it best to just use the POST method, and re-save the Round object in the DB with the changes the user has made?

According to me, the best practice should not be the use of the POST for doing an update/patch.
Keep you POST to do only Round creation.
If you use spring data mongodb just call the save method of your repository with your entity
see https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/MongoRepository.html
For an Update, better to add PUT /{roundId} in your controller and either :
call your save method if you have all the Round data
call a findById to have the full data and set the data you want to change, then save (but this is more like a PATCH)
Or you can also add a PATCH /{roundId} and update just the field you want in your document
see https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoTemplate.html

Related

Generate and set self links in nested items

I should migrate some code from jax-rs to spring mvc. We had a controller, which response with an object and set at the same time links in a list :
HateoasResponse.ok(content)
.selfLink(FieldPath.path("categories"), "some_controller_id", "id")
.build()
Did any one know, if there is something similar in spring mvc ?
I have checked spring-hateoas. If I use it , I should modify my models to something supported by this package (CollectionModel, EnitityModel..)
You have to make the response object extend ResourceSupport and then generate the links as follows.
org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(methodOn(YourSpringMvcController.class)
.methodWhichHasMappingTo(param1,param2,paramN))
.withRel("relationOfThisLinkToTheRequestedResource").expand();
This link can then be added to the response object using the resource add method.
for example, let's say you have a controller like the following:
#RestController
public class OrderController {
#GetMapping(value = "/orders/{orderId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<Order> getOrder(#Valid #PathVariable Integer orderId) {
return getOrder(orderId);
}
#DeleteMapping(value = "/orders/{orderId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<Order> deleteOrder(#Valid #PathVariable Integer orderId) {
return orderRepo.deleteOrder(orderId);
}
}
then for a request to GET orders, you would build the response like the following:
Order which is a response entity will extend ResourceSupport
public Order getOrder(int orderId){
Order order = repo.findByOrderId(orderId);
Link deleteLink = ControllerLinkBuilder.linkTo(methodOn(OrderController.class)
.deleteOrder(orderId))
.withRel("delete").expand();
order.add(deleteLink);
Link selfLink = ControllerLinkBuilder.linkTo(methodOn(OrderController.class)
.getOrder(orderId))
.withSelfRel();
order.add(selfLink);
return order;
}
Hope this helps.

how to achieve Dynamic URL in REST WebService

I am trying to develop new web service for my application.
For that I am using Spring REST-webservice.
In the controller end, I am trying to fetch the list of records based on the agent passed.Now the requirement , the agent can be passed or it can be null.In case of null agent all records should be selected.else only those records to be fetched.
Tried using below code for achieving dynamism., as per one of the search result however it is not working.
#RequestMapping(value = "/staging/{agentCode: [^/]*?}" , method =
RequestMethod.GET)
Here is my existing code:
#Controller
#RequestMapping(value="/batches")
public class BatchController {
#SuppressWarnings({ "rawtypes" })
#RequestMapping(value="/staging/{agentCode}", method =
RequestMethod.GET)
#ResponseBody
public ResponseEntity IntmBatch(#PathVariable("agentCode") String
agentCode)
{
//code here
}
CASE 1:when I use URL like .,
www.example.com/myapplication/batches/staging/1234
it works fine and desired result is fetched.
CASE 2:However in case I am not passing any parameter say.,
www.example.com/myapplication/batches/staging/
where in , I am not passing any parameter., it says me mapping not found.
Can you please let me know how to achieve this dynamic URL in REST GET Request Method Call.
Thanks in advance!!
Instead of using #Pathvariable you can use #RequestParam for optional values in URL.
So your URL will be like.
CASE 1 : www.example.com/myapplication/batches/staging?agentCode=1234 &
CASE 2 : www.example.com/myapplication/batches/staging
Hope it will work solve your issue.
#SuppressWarnings({ "rawtypes" })
#RequestMapping(value="/staging", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity IntmBatch(#RequestParam(name="agentCode",required=false) String agentCode)
{
//code here
}
create one more method in controller with #RequestMapping(value = "/staging", method = RequestMethod.GET) as follows.
#RequestMapping(value = "/staging", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity returnAll() {
System.out.println("returning all ");
// code here
return null;
}

I need documentation about #PostAuthorize method in springboot

#PostAuthorize("returnObject.idea == authentication.idea.ideatorId")
#RequestMapping(value = "/edit/ideaDetails", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> editIdeaByIdeaId(#RequestBody final IdeaDTO idea) throws IOException {
return new ResponseEntity<>(this.facade.editIdeaByIdeaId(idea), HttpStatus.OK);
}
I write only the above code in my controller . The idea is the object of my Idea class and ideatorId is the id for one who creates the idea. My intention is to display only the idea details owned by a particular ideatorId. Is there any other configuration required to use #PostAuthorize or by simply write the above code.

Multiple #RequestMapping (value, method pairs) in Spring MVC

I would like to achieve something like this with Spring MVC
#RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE)
#RequestMapping(value = "/user/{userId}/delete", method = RequestMethod.POST)
public void deleteUser(#PathVariable String userId) {
...
}
This would give me a common endpoint for REST calls and standard HTML form posts.
Is it possible to do with Spring MVC?
All I can come up with is
#RequestMapping(value = { "/user/{userId}", "/user/{userId}/delete"}, method = {RequestMethod.DELETE, RequestMethod.POST})
public void deleteUser(#PathVariable String userId) {
...
}
but the result is slightly different because a POST to "/user/{userId}" would also delete the user.
One thing you could do is make 2 separate methods with their own RequestMapping annotation, and then just pass the parameters on to a different method, where you do actual stuff:
#RequestMapping(value = "/user/{userId}/delete", method = RequestMethod.POST)
public void deleteUserPost(#PathVariable String userId) {
deleteUser(userId);
}
#RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE)
public void deleteUserDelete(#PathVariable String userId) {
deleteUser(userId);
}
private void deleteUser(String userId){
//Do things here
}
Sorry, got it wrong way around.
In mature REST architecture, code should use a URL to refer to a resource and use HTTP method to define the action on the resource. So just define a #RequestMapping("/user/{userId}/delete", method = RequestMethod.DELETE) and eliminate the POST. See DELETE vs POST.

Spring MVC 3.0 Rest problem

I'm trying out Spring MVC 3.0 for the first time and like to make it RESTfull.
This is my controller:
#Controller
#RequestMapping(value = "/product")
#SessionAttributes("product")
public class ProductController {
#Autowired
private ProductService productService;
public void setProductValidator(ProductValidator productValidator, ProductService productService) {
this.productService = productService;
}
#RequestMapping(method = RequestMethod.GET)
public Product create() {
//model.addAttribute(new Product());
return new Product();
}
#RequestMapping(method = RequestMethod.POST)
public String create(#Valid Product product, BindingResult result) {
if (result.hasErrors()) {
return "product/create";
}
productService.add(product);
return "redirect:/product/show/" + product.getId();
}
#RequestMapping(value = "/show/{id}", method = RequestMethod.GET)
public Product show(#PathVariable int id) {
Product product = productService.getProductWithID(id);
if (product == null) {
//throw new ResourceNotFoundException(id);
}
return product;
}
#RequestMapping(method = RequestMethod.GET)
public List<Product> list()
{
return productService.getProducts();
}
}
I have 2 questions about this.
I'm a believer in Convention over Configuration and therefor my views are in jsp/product/ folder and are called create.jsp , list.jsp and show.jsp this works relatively well until I add the #PathVariable attribute. When I hit root/product/show/1 I get the following error:
../jsp/product/show/1.jsp" not found how do I tell this method to use the show.jsp view ?
If I don't add the RequestMapping on class level my show method will be mapped to root/show instead of root/owner/show how do I solve this ? I'd like to avoid using the class level RequestMapping.
add your 'product' to Model and return a String /product/show instead of Product. In your show.jsp, you can access the product object form pageContext
Check out the section in the manual about "Supported handler method arguments and return types".
Basically, when your #RequestMapping method returns just an object, then Spring uses this as a single model attribute, and, I'm guessing, attempts to use the request URL as the basis for the view name.
The easiest way to return the view and data you want from the same method is probably to just have the method return a ModelAndView, so you can explicitly specify the viewName and the model data.

Categories