Assuming I have a controller like:
public class MyController {
public String endpoint1() {...}
public String endpoint2() {...}
}
I want to disable endpoint1 for whatever reason in Spring. Simply, just disable it so that it cannot be accessed. So, I am not looking for how and what response to return in that case or how to secure that endpoint. Just looking to simply disable the endpoint, something like #Disabled annotation on it or so.
SOLUTION UPDATE:
Thanks all who contributed. I decided to go with #AdolinK suggestion . However, that solution will only disable access to the controller resulting into 404 Not Found. However, if you use OpenApi, your controller and all of its models such as request/response body will still show in swagger.
So, in addition to Adolin's suggestion and also added #Hidden OpenApi annotation to my controllers like:
In application.properties, set:
cars.controller.enabled=false
Then in your controller, use it. To hide controller from the OpenApi/Swagger as well, you can use #Hiden tag:
#Hidden
#ConditionalOnExpression("${cars.controller.enabled}")
#RestController
#RequestMapping("/cars")
public class Carontroller {
...
}
After this, every end point handled by this controller will return 404 Not Found and OpenApi/Swagger will not show the controllers nor any of its related schema objects such as CarRequestModel, CarResponseModel etc.
You can use #ConditionalOnExpression annotation.
public class MyController {
#ConditionalOnExpression("${my.controller.enabled:false}")
public String endpoint1() {...}
public String endpoint2() {...}
}
In application.properties, you indicates that controller is enabled by default
my.controller.enabled=true
ConditionalOnExpression sets false your property, and doesn't allow access to end-point
Why not remove the mapping annotation over that method?
Try this simple approach: You can define a property is.enable.enpoint1 to turn on/off your endpoint in a flexible way.
If you turn off the endpoint, then return a 404 or error page, which depends on your situation.
#Value("${is.enable.enpoint1}")
private String isEnableEnpoint1;
public String endpoint1() {
if (!"true".equals(isEnableEnpoint1)) {
return "404";
}
// code
}
Related
Is there an option to specify a request header once in spring web RestController instead of doing it on every request?
e.q.
#RestController("workflowController")
public class MyClass{
public Value list(#RequestHeader(USER_ID_HEADER_PARAM) String user) {
...some code
}
public Workflow create(#RequestBody Workflow workflow, #RequestHeader(USER_ID_HEADER_PARAM) String user) {
... some code
}
}
the #RequestHeader(USER_ID_HEADER_PARAM) will be repeated in every request.
is there a way to specity it in the #RestCotroller level or the class level?
Thanks
Use some kind of filter class that can be configured to wrap around your requests in your servlets based on the URL path.
Here is info about the generic Servlet API filter API:
https://www.oracle.com/technetwork/java/filters-137243.html
If you're using Spring, there's another way to do it:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#filters
https://www.baeldung.com/intercepting-filter-pattern-in-java
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.
Here is my resource
#Path("test")
#DenyAll
public class TestResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response test() {
return Response.status(Response.Status.OK).entity("ok").build();
}
}
When I run the application and call GET /test the response is sent.
I am kind of confused, is there something else to do in addition to the annotation? Am I supposed to deny access myself in a filter?
When I use #RolesAllowed() I don't have to implement anything...
Thanks.
If you look at the source code for RolesAllowedDynamicFeature, you will see two two thing:
DenyAll is never checked for on classes.
There is a comment // DenyAll can't be attached to classes
Please find my HomeController and DemoController
class HomeController{
#RequestMapping(value="index")
public void home(){
}
}
class DemoController{
#RequestMapping(value="index")
public void demo(){
}
}
when I try to send a request to index, which one will get executed?
I wanted to know how can we have same request mapping value for multiple controllers
https://stackoverflow.com/a/34590355/2682499 is only partially correct at this point.
You can have multiple controller methods use the same URI so long as you provide Spring enough additional information on which one it should use. Whether or not you should do this is a different question. I would certainly not recommend using the same URI in two separate controller classes to avoid confusion, though.
You can do something like this:
class HomeController{
#RequestMapping(value="/index", params = {"!name", "!foo"})
public List<Something> listItems(){
// retrieve Something list
}
#RequestMapping(value="/index", params = "name")
public List<Something> listItems(String name) {
// retrieve Something list WHERE name LIKE %name%
}
#RequestMapping(value="/index", params = {"!name", "foo"})
public List<Something> listItems(String foo) {
// Do something completely different
}
}
For the full documentation on what is possible when overloading URIs you should reference the #ReqeustMapping documentation: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html. And, specifically https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params-- for the section request parameters.
In Spring Web MVC this is not possible. Each mapping must be unique in your context. If not, you will receive a RuntimeException during context initialization.
You cannot even use parameters to differentiate your endpoints because they are not evaluated while searching for a suitable handler (applicable for Servlet environments). From #RequestMapping javadoc:
In a Servlet environment, parameter mappings are considered as restrictions that are enforced at the type level. The primary path mapping (i.e. the specified URI value) still has to uniquely identify the target handler, with parameter mappings simply expressing preconditions for invoking the handler.
Note that you can do the opposite, so multiple URLs can point to the same handler. Have a look at Spring MVC: Mapping Multiple URLs to Same Controller
Unfortunately, this is not possible. The request mapping has to be unique otherwise the application can't determine which method the incoming request should be mapped to.
What you can do instead is to extend the request mapping:
class HomeController{
#RequestMapping(value="home/index")
public void home(){
}
}
class DemoController{
#RequestMapping(value="demo/index")
public void demo(){
}
}
Plain and simple issue:
I would like to annotate my method parameter with some sort of annotation that would fire regular spring validation mechanism (based on Validator interface)
I don't want to include JSR303 dependency
Any ideas? I looked on #Validated but it seems that it was not created for this purpose.
Right now I do it like this:
public String req(#ModelAttribute SomeRequest request, BindingResult errors) {
validator.validate(request, errors); // This can be avoided
if (!errors.hasErrors()) {
// Valid request
return ...
} else {
// There were errors
return ...
}
}
In that case you need to create custom annotation using #interface and then use that instead of standard #Valid annotation and at runtime identify it and validate your fields accordingly. Hope this helps you. Cheers.