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.
Related
First of all, thank you in advance for your support.
My problem;
First I am successfully getting my specific parameters in Employer. However, I also have a constantly changing parameter list in request. I want to get them with Map too.
My dto:
public class Employee {
private String name;
private MultipartFile document;
}
#RequestMapping(path = "/employee", method = POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public Mono<Employee> saveEmployee(#ModelAttribute Employee employee, Map<String,Object> otherValues) {
System.out.println(otherValues.get("key1").toString());
return employeeService.save(employee);
}
I attached a request example aslo.
NOTE:
I used #RequestParam, #RequestPart before Map<String,Object> otherValues like this;
#RequestParam Map<String,Object> otherValues
#RequestPart Map<String,Object> otherValues
But I still couldn't get the rest of the data.
When you want to get values from form data, you can create a model like a DTO, with field of both entity (employee) and additional data (otherValues). Because #ModelAttribute can bind data to only one named model attribute
This is working for Spring MVC. But When you work with Spring WebFlux for reactive applications, it doesn't work.
#RequestMapping(path = "/employee", method = POST, consumes ={MediaType.MULTIPART_FORM_DATA_VALUE })
public Employee saveEmployee(
#ModelAttribute Employee employee,
#RequestParam Map<Object,Object> otherValues,
#RequestParam("file") MultipartFile file) {
System.out.println(otherValues.get("key1").toString());
return employeeService.save(employee);
}
Main trick is to use Object for key and value;
#RequestParam Map<Object,Object> otherValues
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
What is the difference between below two attributes and which one to use when?
#GetMapping(path = "/usr/{userId}")
public String findDBUserGetMapping(#PathVariable("userId") String userId) {
return "Test User";
}
#RequestMapping(value = "/usr/{userId}", method = RequestMethod.GET)
public String findDBUserReqMapping(#PathVariable("userId") String userId) {
return "Test User";
}
As mentioned in the comments (and the documentation), value is an alias to path. Spring often declares the value element as an alias to a commonly used element. In the case of #RequestMapping (and #GetMapping, ...) this is the path property:
This is an alias for path(). For example #RequestMapping("/foo") is equivalent to #RequestMapping(path="/foo").
The reasoning behind this is that the value element is the default when it comes to annotations, so it allows you to write code in a more concise way.
Other examples of this are:
#RequestParam (value → name)
#PathVariable (value → name)
...
However, aliases aren't limited to annotation elements only, because as you demonstrated in your example, #GetMapping is an alias for #RequestMapping(method = RequestMethod.GET).
Just looking for references of AliasFor in their code allows you to see that they do this quite often.
#GetMapping is a shorthand for #RequestMapping(method = RequestMethod.GET).
In your case.
#GetMapping(path = "/usr/{userId}") is a shorthand for #RequestMapping(value = "/usr/{userId}", method = RequestMethod.GET).
Both are equivalent. Prefer using shorthand #GetMapping over the more verbose alternative. One thing that you can do with #RequestMapping which you can't with #GetMapping is to provide multiple request methods.
#RequestMapping(value = "/path", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT)
public void handleRequet() {
}
Use #RequestMapping when you need to provide multiple Http verbs.
Another usage of #RequestMapping is when you need to provide a top level path for a controller. For e.g.
#RestController
#RequestMapping("/users")
public class UserController {
#PostMapping
public void createUser(Request request) {
// POST /users
// create a user
}
#GetMapping
public Users getUsers(Request request) {
// GET /users
// get users
}
#GetMapping("/{id}")
public Users getUserById(#PathVariable long id) {
// GET /users/1
// get user by id
}
}
#GetMapping is an alias for #RequestMapping
#GetMapping is a composed annotation that acts as a shortcut for #RequestMapping(method = RequestMethod.GET).
value method is an alias for path method.
This is an alias for path(). For example #RequestMapping("/foo") is equivalent to #RequestMapping(path="/foo").
So both methods are similar in that sense.
I want to create a REST-GET controller in spring-mvc that takes a list of objects, eg 10 ids as follows:
#RestController
public class MyRest {
#RequestMapping(method = RequestMethod.GET)
public Object test(#RequestParam value="id" required=false) List<Integer> ids) {
Sysout(ids);
}
}
Anyway when I call it, I have to repeat the id param multiple times:
localhost:8080/app?id=1&id=2&id=3&...
It is possible to change the param to some kind of list? Eg
app?id=1,2,3,4,5
And if yes, is this advisable? What's better from the client point of view?
Its better to use POST message with JSON or XML as request body.
As you never know how many id's will be passed.
#RestController
public class MyRest {
#RequestMapping(method = RequestMethod.POST)
public Object test(#RequestBody IDRequest request) {
Sysout(ids);
}
public static final class IDRequest {
List<Integer> ids;
<!-- getter/setters--->
}
}
where the request will be some kind of a JSON or XML like this
{"ids":[1,2,3,4,5,6,7,8,9]}
You can provide list of objects to rest service as request param.Here is the example
#RequestMapping(value = "/animals, method = RequestMethod.GET)
public void test(#RequestParam(value="animalsNames[]") String[] animalsNames) {
Sysout(animalsNames);
}
And your request looks like
http://localhost:8080/appname/animals?animalsNames[]=dog,horse
HTTP Method type : GET
Controller :
public #ResponseBody String getInfos(HttpServletRequest request,
#RequestParam #DateTimeFormat( #RequestParam List<Long> ids) {...}
Request :
http://localhost:8080/test/api?ids=1,2,3
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.