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

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.

Related

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

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) {
...
}
}

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.

Creating a custom controller parameter automatically populated with HttpServletRequest?

Using a strictly annotation-based/Java-only Spring MVC 3.2.2 configuration, I'm trying to create a controller method with a custom class (I'll call it Context) as a parameter. I want to have Context constructed with knowledge of the current HttpServletRequest and then passed along to the controller method. In essence, I want to create my own custom wrapper around the request object before it's sent to the controller. e.g. I want to accomplish this:
#Controller
#RequestMapping(value = "/")
public class MainController {
#RequestMapping(value = "/")
public #ResponseBody
String process(HttpServletRequest request) {
Context context = new Context(request);
...
}
}
automatically like this:
#Controller
#RequestMapping(value = "/")
public class MainController {
#RequestMapping(value = "/")
public #ResponseBody
String process(Context context) {
...
}
}
Is this possible? I looked into implementing a HandlerMethodArgumentResolver as a #Bean in my WebMvcConfigurerAdapter but I don't think that's the correct route to take. I've tried adding #AutoWired to Context (as a #Bean) to no avail as well. I imagine there's WebMvcConfigurerAdapter or possibly in a AbstractAnnotationConfigDispatcherServletInitializer?
HandlerMethodArgumentResolver and WebMvcConfigurerAdapter is certainly a right way to achieve your goal.
In order to register custom argument resolver you need to make your #Configuration extend WebMvcConfigurerAdapter and override its addArgumentResolver() method.

Binding a Backbone.js object to a Spring Controller while utilising #InitBinder

I am having trouble getting #InitBinder to work with Backbone.js
#InitBinder
public final void initBinder(final WebDataBinder binder) {
// Customer property editors
binder.registerCustomEditor(MyObjectChild.class, new MyObjectChildEditor());
}
I have a controller like this that accepts the object
#RequestMapping(
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value = HttpStatus.CREATED)
public final String create(#RequestBody final MyObject myobject) {
return "worked";
}
MyObject has a property of type MyObjectChild. I would like to pass the ID values of this object from the frontend to the controller because it does not make sense for the user to have the ability to modify it (they will select it from the drop down list).
I have read that #RequestBody does not work with #InitBinder so I tried #ModelAttribute instead, but I don't know what the #ModelAttribute name would be because it is created on the frontend in javascript using Backbone.js.
How do I get Backbone.js to work with Spring #InitBinder?

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