In our REST-API we need to be multi-tenant capable. For achiving this all rest controllers subclass a common REST controller which defines a request mapping prefix and exposes a model attribute as follows
#RequestMapping(path = "/{tenantKey}/api")
public class ApiController {
#ModelAttribute
public Tenant getTenant(#PathVariable("tenantKey") String tenantKey) {
return repository.findByTenantKey(tenantKey);
}
}
Derived controllers make use of the model attributes in their request mapping methods:
#RestController
public class FooController extends ApiController {
#RequestMapping(value = "/foo", method = GET)
public List<Foo> getFoo(#ApiIgnore #ModelAttribute Tenant tenant) {
return service.getFoos(tenant);
}
}
This endpoint gets well documented in the swagger-ui. I get an endpoint documented with a GET mapping for path /{tenantKey}/api/foo.
My issue is, that the {tenantKey} path variable is not documented in swagger-ui as parameter. The parameters section in swagger is not rendered at all. If I add a String parameter to controller method, annotating it with #PathVariable("tenantKey) everything is fine, but I don't want a tenantKey parameter in my controller method, since the resolved tenant is already available as model attribute.
So my question is: Is there a way do get the #PathVariable from the #ModelAttriute annotated method in ApiController documented within swagger-ui in this setup?
Project-Setup is
Spring-Boot (1.4.2)
springfox-swagger2 (2.6.1)
springfox-swagger-ui (2.6.1)
This is certainly possible. Model attributes on methods are not supported currently. Instead, you could take the following approach.
Mark the getTenant method with an #ApiIgnore (not sure if it gets treated as a request mapping.)
In your docket you can add tenantKey global path variable (to all end points). Since this is a multi-tenant app it's assuming this applies to all endpoints.
Related
How the request body of form is passed to the spring MVC controller and how to map to the POJO class instance?
I presume, you are building end point using POST. If yes, you can annotate method parameter with #RequestBody to a capture request body.
Basically, #RequestBody is used to bind the HTTP request body with a domain object in method parameter. Behind the scenes, these annotation uses HTTP Message converters to convert the body of HTTP request to domain objects.
#RequestMapping(value="/user/create", method=RequestMethod.POST)
public void createUser(#RequestBody User user){
// your logic goes here..
// Make sure that parameters in User POJO matches with HTTP Request Parameters.
}
I assume you are using POST API request for your use case. In the spring mvc controller class, we can make use of #RequestBody annotation followed by Pojo class.
Example:
#PostMapping
public ResponseEntity<ResponseDto> saveData(#RequestBody Data data) {
// Access to service layer
}
Let's say you're doing a POST request.
#PostMapping
public void saveSomeData(#RequestBody PojoClass pojoClass){
// whatever you wanna do
}
The #RequestBody annotation extracts the data your client application is sending on the body of the request. Also, you'd want to name the member variables on your pojo class similar to how you named it in on your request body.
Your POJO class can be something like:
public class PojoClass{
private String name;
private int age;
}
Now you can create getters and setters or use the Lombok annotation #Data on the class level to auto-generate the getters and setters.
Hope this was helpful:)
I am working on a Spring MVC application and I have the following problem into a controller class that is annotated with #RequestMapping("/profilo/") at class level.
So I have something like this:
#Controller
#RequestMapping("/profilo/")
public class ProfiloController extends BaseController {
..................................................................
..................................................................
SOME CONTROLLER METHOD THAT WORK CORRECTLY
..................................................................
..................................................................
#RequestMapping(value = "utenze/{username}/confermaEmail/{email:.+}", method = RequestMethod.GET)
public String confermaModificaEmail(#PathVariable String username, #PathVariable String email, Model model) {
logger.debug("INTO confermaModificaEmail(), indirizzo e-mail: " + email);
.......................................................................
.......................................................................
.......................................................................
}
}
So, as you can see in the previous code snippet, I have this ProfiloController class that is annotated with #RequestMapping("/profilo/") so it means that all the HTTP Request handled by the controller method of this class have to start with the /profilo/ in the URL.
It is true for all the method of this class except for the confermaModificaEmail() method that have to handle URL as:
http://localhost:8080/my-project/utenze/mario.rossi/confermaEmail/a.nobili#siatec.net
that don't tart with /profilo/.
So can I specify that for this specific method of the controller the #RequestMapping("/profilo/") controller level mapping is not valid and have to be not considered?
This is not possible.
Spring has maintained a proper #Controller structure which says for all endpoints related to ControllerName (i.e .Portfolio in your case) should be kept in this class.
Ideally, any other url not related to the functionality should be kept as part of a different Controller class.
If still you want to keep in same controller, try calling the endPoint url confermaModificaEmail() by redirecting the http call from "/portfolio/<"sample"> to the required url. But this is not recommended.
I have few methods in my spring controllers which are mapped on the same path, example.
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
protected ResourceDTO getById(#PathVariable int id) {
return super.getById(id);
}
I was wondering if there is a way to create an annotation that will automatically have set value and method, to have something like this:
#RequestMappingGetByID
protected ResourceDTO getById(#PathVariable int id) {
return super.getById(id);
}
Have a nice day everyone
Update
The goal of this is the following
all my controllers (eg. user, order, client) extends a parametrized BaseController that includes a base set of function (get by id, save, update, delete, etc) All the logic is on the BaseController, but in order to map the value I have to add the annotation on the specific controller.
Instead of writing all the time {id} and post I would like to annotate the methods with a custom interface that already includes those values
The following works for Spring 4.1.x that I tested:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#interface RequestMappingGetByID {
}
Then you can use
#RequestMappingGetByID
protected ResourceDTO getById(#PathVariable int id) {
return super.getById(id);
}
like you mention.
This kind of annotation is was Spring calls a meta-annotation. Check out this part of the documentation
I am not sure if this meta-annotation would work in versions of Spring prior to 4.x, but it's definitely possible since Spring had some meta-annotation handling capabilities in the 3.x line
If you where using Groovy, you could also take advantage of the #AnnotationCollector AST, which in effect would keep the duplication out of your source code, but would push the regular #RequestMapping annotation into the produced bytecode. Check out this for more details.
The benefit in this case would be that Spring need not have to be equipped with the meta-annotation reading capabilities, and there for the solution possibly works on older Spring versions
I am trying to build RESTful web service by using spring 4.0
I have a controller:
#Controller
#RequestMapping("test")
public class Controller{
#RequestMapping("fetch",method=RequestMethod.GET)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public ResultResponse fetchController(ResultRequest req){
if((req.getName).equals("John"))
return new ResultResponse(100);
else
return new ResultResponse(0);
}
}
and my ResultRequest.class
public class ResultRequest{
private String name;
//getter,setter
}
If I hit the url to //host//contextPath/test/fetch?name=John
the controller will return the object ResultResponse(100)
my question is, there no #RequestParam or other annotation in the request parameter,
how does the spring controller know to set the query parameter "name" as the property of wrapper class
ResultRequest ?
Thanks
Spring uses implementations of an interface called HandlerMethodArgumentResolver for resolving arguments to pass to handler methods, ie. methods annotated with #RequestMapping.
One of these is a ModelAttributeMethodProcessor. Its javadoc states
Resolves method arguments annotated with #ModelAttribute and handles
return values from methods annotated with #ModelAttribute.
Model attributes are obtained from the model or if not found possibly
created with a default constructor if it is available. Once created,
the attributed is populated with request data via data binding and
also validation may be applied if the argument is annotated with
#javax.validation.Valid.
When this handler is created with annotationNotRequired=true, any
non-simple type argument and return value is regarded as a model
attribute with or without the presence of an #ModelAttribute.
Spring registers two objects of this type. One to handle parameters annotated with #ModelAttribute and one to handle ones without.
Further reading:
Form submit in Spring MVC 3 - explanation
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments
I have a controller which accepts a form post -
#Controller
public class RegistrationFormController extends SimpleFormController {
.....
.....
#RequestMapping(value="index", method=RequestMethod.POST)
protected ModelAndView onSubmit(#ModelAttribute Registration registration) throws Exception {
String uname=registration.getUsername();
.....
.......
ModelAndView mv = new ModelAndView("success");
.....
......
mv.addObject("addr",addr);
return mv;
}
Thsi would work just as well even if i do not extend SimpleFormController.
What then can I gain by extending?
Well, the setup seems not good. The #Controller annotation is a nice feature which is used for declaring stereotypes. It just says that it is another spring Component or Spring managed bean and can be detected in component scan.
Where as when you extend SimpleFormController you explicitly say that it is a Controller and it has to be used as a controller, it will be used to accept submitted form data and return a response in form of a view.
The two notations in the same class makes no sense at all, I feel that making a class SimpleFormController restricts it from using any method name and you are forced to use a onSubmit method. Whereas, if you use #Controller you leverage all the flexibility in Spring 3 and above.