Create entities with custom mime type as addition to spring data rest controller - spring-mvc

I have #Entity called Scenario. I have a #Repository for it and spring data rest which generates MVC controller for the CRUD operations. The path is /api/scenarios. Works.
I want to be able to POST to the /api/scenarios endpoint not only with the default JSON but also with multipart/form-data so I can create my Scenario from a file which the user uploads. I tried creating custom #Controller:
#RestController
public class ImportController {
#ResponseStatus(HttpStatus.CREATED)
#RequestMapping(value = "/api/scenarios", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> uploadScenario(#RequestPart(name = "scenario") MultipartFile scenarioFile) {
...
}
}
With this the upload for multipart/form-data works but I can no longer access the enpoint with GET. I'm getting Method not allowed. Like if my custom controller hides the one generated by spring data rest.
Is there a way how to add such custom POST on top of the existing controller so I can create my Scenarios with both JSON and application/form-data mime types?

You need to specify this custom controller as repository controller than Spring will add your custom methods to that generated controller.
Please note, you don't need to add api base path to your controller mapping. So, your mapping here is "/scenarios", not "/api/scenarios"
#RepositoryRestController
#RequestMapping("/scenarios")
public class ImportController {
#ResponseStatus(HttpStatus.CREATED)
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> uploadScenario(#RequestPart(name = "scenario") MultipartFile scenarioFile) {
...
}
}

Related

Multiple Get methods in Rest API controller

Hello I have added an extra get method in a API controller.
original Get
[HttpGet]
[Route("GetParticipants")]
public IActionResult GetParticipants([FromQuery] Guid conversationId, [FromQuery] ContextServiceModel context)
{
... stuff ...
}
New Get
[HttpGet]
[Route("GetThreadParticipants")]
public IActionResult ThreadParticipants([FromQuery] Guid parentMessageId)
{
... stuff ...
}
My question is does this follow Rest? Is it okay to route them this way and have different parameters?
You can have as many Action methods as you want in your controller until and unless no action methods have same routes.
Routing
Conventional Routing
Defined in WebAPI.config
Attribute Routing
Directly defined on action method
More on attribute routing
Attribute routing Web API's

How to validate url parameters bundle in parameter object in Spring's #RestController

I have a standard #RestController method. I introduced a parameter-object (MyMapQuery) in order to avoid large number of method arguments:
#RestController
public class MyController {
#RequestMapping(value = "/api/search")
#ResponseBody
public SearchResponse search(MyMapQuery query) {
[...]
}
MyMapQuery is a standard java bean with setters and getters, so when I pass multiple url parameters http://.../api/search?west=1&east=2&north=20&south=0, they are correctly filled in.
How do I implement required validation on some of the url parameters?
Spring automatically responds with 400(bad request), when parameters are mapped like below, but does no validation in case of MyMapQuery.
public SearchResponse search(#RequestParam BigDecimal east, #RequestParam BigDecimal west, ...) {
Just use
public SearchResponse search(#Valid MyMapQuery query) {
and add the necessary bean-validation annotations to the fields of MyMapQuery.

Specifying #RequestHeader once for all controllers in Spring Boot app

I have a Spring Boot app with multiple controllers serving various REST methods. Each of the methods require that the same header parameter be defined. Is there a way to specify something like the following one time for all controller methods?
public ResponseEntity get(#RequestHeader(value="NAME", required = true) String name, ...) {
...
}
Thanks.
You can probably achieve this using #ModelAttribute, like this:
public class Something {
private name;
//...
}
#ModelAttribute("something")
public Something addSomething(#RequestHeader(value="NAME", required = true) String name) {
return new Something(name);
}
#RequestMapping("/something")
public ResponseEntity get(#ModelAttribute Something something) {
//...
}
You can implement the #ModelAttribute populating method in a single Controller or in a #ControllerAdvice class, in order to assist multiple controllers. See reference documentation.

New implementation/method of existing request mapping in controller

I am trying to look for a way to extend a controller such that I can use the existing request mapping but with a different implementation/method tied to it.
Say for example, below is a code snippet from AdminBasicEntityController in BLC where viewAddEntityForm method is tied to /add request mapping. Now I want to have my own logic of showing entityForm(Say Product entity) using /add request mapping. Is it possible?
#Controller("blAdminBasicEntityController")
#RequestMapping("/{sectionKey:.+}")
public class AdminBasicEntityController extends AdminAbstractController {
#RequestMapping(value = "", method = RequestMethod.GET)
public String viewEntityList(HttpServletRequest request, HttpServletResponse response, Model model,
#PathVariable Map<String, String> pathVars,
#RequestParam MultiValueMap<String, String> requestParams) throws Exception {
// default implementation
}
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String viewAddEntityForm(HttpServletRequest request, HttpServletResponse response, Model model,
#PathVariable Map<String, String> pathVars,
#RequestParam(defaultValue = "") String entityType) throws Exception {
// default implementation
}
}
Also I found below mentioned information in the documentation of AdminBasicEntityController, so does it mean I can have controller for specific entity. If yes, how?
The default implementation of the {#link #BroadleafAdminAbstractEntityController}. This delegates every call to super and does not provide any custom-tailored functionality. It is
responsible for rendering the admin for every entity that is not
explicitly customized by its own controller
#RequestMapping("/{sectionKey:.+}") uses the generic path variable sectionKey, making the controller handle any requests that are not explicitly mapped. For example,
/product
/product/add
/category
/category/add
/store
/store/add
may all be hitting this controller if these URLs have not been explicitly mapped to their own controllers.
To handle a specific URL yourself, you could do:
#Controller
#RequestMapping("/product")
public class ProductAdminController {
#RequestMapping("/add")
public String viewAddEntityForm(...) { ... }
}
Now, the URL /product/add will be routed to this custom controller while all others will continue to get routed to the generic controller.

Whats the use of mapping #requestMapping onto an entire class?

This is an extremely basic question about spring MVC i have seen a few examples where the #RequestMapping sits above the class name of a Controller :
#RequestMapping
public class somethingController {
.
.
.
}
I understan the use of RequestMapping when it comes to methods but i haven't been able to understand the use of mapping it onto an entire class. What is it used for?
thanks in advance.
It allows mapping all the methods to a URL, or a URL prefix, or other restrictions. Further restrictions (like POST/GET, or URL suffix, etc.) can then be defined by a RequestMapping annotation on the methods. These method-level restrictions will either complement or override the restrictions placed on the type-level annotation.
The attributes that can be used at class or method or both levels, and how they behave, are specified in the javadoc.
For example:
#RequestMapping(value = "/foo", produces = "test/html")
public class SomeController {
#RequestMapping(method = RequestMethod.GET)
public String method1() {
...
}
#RequestMapping(method = RequestMethod.POST)
public String method1() {
...
}
}
In this example, both methods are mapped on /foo and produce HTML, but the first one is called when the HTTP method is GET, whereas the second one is called when the HTTP method is POST.

Resources