Spring boot - Controller catching all URLs - java

I am building a Spring boot web app and am using annotations for controller/url mapping.
I have several controllers annotated with #RequestMapping with the url value set (both empty strings and specific URLs) which are working fine e.g.
#Controller
#RequestMapping("/accounts")
class SignInController {
#Autowired PartyService partyService
#RequestMapping(value="", method = RequestMethod.GET )
public String signinPage( Model model) {
Navigating to /accounts renders the sign-in page correctly.
However, if I add a controller with no RequestMapping values e.g.
#Controller
class CustomController {
#RequestMapping
public String transform( Model model ) {
Then any URL I enter that doesn't match any other specific controller is getting handled by this controller (so pages I would expect to 404 all just renderthis page). Is this expected behaviour? I was not expecting this, and as the RequestMapping value defaults to empty and is an antMatcher I wouldn't have thought it would handle all other URLs.
The reason I have this controller with out RequestMapping defined is because I want to also have a SimpleUrlMappingHandler defined with some explicit URLs going to that controller, and if I don't include the #Controller & #RequestMapping annotations to that controller then I get an error about not being able to find the handler method (maybe the problem is that I have mis-understood the implementation details of that).
Should my custom controller be handling all URLs? If so, is there something I can do so it doesnt and only gets called for the explicit SimpleUrlMappingHandler I have defined?

As mentioned in the comments - I just removed the #Controller annotation from the class, and then explicitly defined the controller as a #Bean in my config class and explicitly assigned that to the mapping in the SimpleUrlMappingHandler configuration

Related

html file not returning in the Rest API and return the file name only like 'index' in the screen for the following code when used #RestController

html file not returning in the Rest API and return the file name only like 'index' for the following code when used #RestController and working fine only for #Controller.
Here I am using spring boot REST application and index.html with bootstrap
If you know REST web services then you must know the fundamental difference between a REST API and a web application i.e. the response from a web application is generally view (HTML + CSS) because they are intended for human viewers.
REST API just returns data in form of JSON or XML because most of the REST clients are programs. This difference is also obvious in the #Controller and #RestController annotation.
#RestController = #Controller + #ResponseBody
When you use #Controller annotation then it corresponds to MVC workflow in Spring Boot and it renders the view. The key difference is that you do not need to use #ResponseBody on each and every handler method once you annotate the class with #RestController.
#ResponseBody is a Spring annotation which binds a method return value to the web response body. It is not interpreted as a view name. It uses HTTP Message converters to convert the return value to HTTP response body, based on the content-type in the request HTTP header. The content type can be JSON or XML.
It is the expected behaviour of #RestController. The main difference between #RestController and #Controller is #RestController will by-pass the view resolution but #Controller will not.
Technically , it is due to the presence of the #ResponseBody of the #RestController. #ResponseBody will enable RequestResponseBodyMethodProcessor to process the result return from the #RestController method and it will mark that the request has been fully handled within the controller method and by-pass the view resolution process (see this).

Swagger documents all those HTTP methods which weren't mapped in Spring Boot Controller class

I'm using Swagger 3.0.0 for documentation of the HTTP API endpoints generated through my Spring Boot based application. However, when I mapped a controller method to an endpoint, then Swagger created documentation in which it associated that controller with all the HTTP methods possible(even though I explicitly programmed it to be associated with a single HTTP method, say GET). For instance, consider the following code :
// Application.java
//required imports
#SpringBootApplication
#EnableSwagger2
public class Application {
public static void main (String args) {
SpringApplication.run(Application.class, args);
}
#Bean
public Docket apiConfigurator () {
// build the required Docket
}
}
// DataController.java
// required imports
#RestController
public class DataController {
#ApiOperation(value = "Returns all the data")
#RequestMapping("/data/allData")
public String getAllData () {
// return some data
}
}
Now, if I were to execute something like this, then Swagger will automatically map the getAllData () method to all HTTP methods (GET, PUT, POST, etc). And this is wrong, because the only method that getAllData () should be associated with is a basically a GET method.
So, how should I take care of this issue?
I don't know about the specifics, but directly using #RequestMapping annotation for implicitly mapping a GET method to a controller method confuses Swagger. That's why you need to be specific when you're writing down a mapping annotation in presence of Swagger. What I mean to say is that, you need to explicitly tell Spring Boot (and Swagger) that this controller method, say getAllData () is mapped to a HTTP method, say GET by using either :
#RequestMapping (value = "/path/to/get/foobar", method = RequestMethod.GET) or
#GetMethod ("/path/to/get/foobar")
This way the Swagger based documentation associates getAllData () with only GET and nothing else.

AspectJ with Spring : intercepted methods lose their parameters annotations

I recently added AOP with aspectJ and spring-aop to my existent spring project. The goal was to actually intercept controller calls to modify the response they send back, in order to bind some values to this response I didn't want to add manually to each and everyone of my controllers, for example the expiration date of the actual token used by the end-user (which I wasn't even able to showcase within my controller in any case). I actually managed to get it working until I started my unit tests :
In my unit tests I call directly my controller methods using Reflection feature from java, then replicate usual process (calling the filter chain, pre handler and post handlers, and the controller method itself which is first manually validated using spring validator when annotation #Valid is present on one of my parameters. All this process works fine and gets executed properly). The problem is that now that the controller method is intercepted by spring-aop, it's mentionned as coming from the proxy controller created, and all of my parameters annotations disapear. Here is a controller example :
#Override
public ResponseEntity<Object> editPassword(#Valid #RequestBody PasswordEditForm passwordEditForm, HttpServletRequest request) {
return factorizedUserBaseController.editPassword(passwordEditForm, request, User.class);
}
the parameter PasswordEditForm has the annotation #Valid so in my test cases it was first validated before any other step, but now as I double checked it, the #Valid annotation is not present on the proxy method, and therefore the parameter doesn't get validated, any clue for how to fix this and make my parameters annotation still understandable from my test point of view?
Note : when running the spring through mvn spring-boot:run, parameters with #Valid annotation gets correctly validated and then goes to my error handler method properly.
Problem Solved : from several other stackoverflow posts I understand that CGLIB (aop proxy lib used by Spring) doesn't support annotations. ( see Retain annotations on CGLIB proxies?). But my problem wasn't here, I was literally sure I was finding the method using the controller class itself (the one I coded) but what I was wrong about is that I was giving the controller instance as a parameter to some other parts of my code which in turn would use this controller class to find the method which of course wasn't working because thanks to Spring proxies, it wasn't anymore my controller itself but a proxy class extending my own controller class. Instead, I just had to replace :
Class<?> controllerClass = controllerInstanciationContainer
.getController()
.getClass();
with
Class<?> controllerClass = controllerInstanciationContainer
.getController()
.getClass()
.getSuperclass();

Spring MVC custom authorization/security for controller methods

Looking to create a custom implementation to authorize requests.
I am evaluating options to avoid using annotations on every method in the controller.
Instead, I am exploring the possibility centralizing this feature via a filter that checks if the logged in user has the required permission for the URL
We are using Spring 3.2.5.RELEASE
I intend to create a database table that has a mapping between a permission and the relevant URL.
I am looking for a way to get the request mapping information for the current URL.
For E-g :
In my database, I have:
URL=/view/{id} , permission=VIEW_USER
If the current URL is :
/app/user/view/1
, a method annotated with
#RequestMapping(value = "/view/{id}", method = RequestMethod.GET)
will be invoked. But before this happens, I need to verify if the logged in user has the permission to view user details.
In my filter, I can get the current URL (/app/user/view/1) , how do I get the corresponding mapping (/view/{id}) ? Is there a mechanism to match a URL to its corresponding MVC request mapping ?
I have looked/am looking at related posts on SO and also looked at Spring code but am yet to find a way.
If you want to do it that way, you could register MVC interceptor instead of servlet filter.
Create a class that extends HandlerInterceptorAdapter and in preHandle method you will have access to controller's method and it's annotation. Prehandle method of your interceptor could look something like this:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
if (method.getMethod().isAnnotationPresent(RequestMapping.class)) {
RequestMapping rqm = method.getMethodAnnotation(RequestMapping.class);
String[] urlMappings = rqm.value();
//do security logic
}
}
...
}
Then you need to register the interceptor. If you use xml config it's done like this:
<mvc:interceptors>
<bean class="com.example.MySecurityInterceptor" />
...
</mvc:interceptors>
Please note that your approach will be difficult, you'll need to handle all the request mapping cases that spring supports. For example, #RequestMapping that's annotated on class level. Or #RequestMapping annotated on parent class of the controller etc..

Spring #Controller Separating GET and POST mappings

I am using Spring MVC with annotation configuration. I have a controller class for handling HTTP GET calls:
#Controller
#RequestMapping("/form")
public class FormController {
#RequestMapping(value = "/{table}/{identifier}/edit", method = RequestMethod.GET)
public ModelAndView getEditView(ModelMap map, #PathVariable String table, #PathVariable Object identifier) {
//generate the view for this record
}
and a Controller for processing form submits on that URL
#Controller
#RequestMapping("/form")
public class FormSaveController {
#RequestMapping(value = "/{table}/{identifier}/edit", method = RequestMethod.POST)
public ModelAndView saveView(WebRequest request, #PathVariable String table, #PathVariable Object identifier) {
//save the updated values and redirect to view
}
When I attempt to startup my container, spring complains
Caused by: java.lang.IllegalStateException: Cannot map handler 'FormSaveController' to URL path [/form/{table}/{identifier}/edit]: There is already handler of type [class com.company.web.FormController] mapped.
This seems to indicate what I'm trying to do is not supported in Spring. The reason I am trying to separate the controller for generating the form from the controller saving the form because I am using Springs #ExceptionHandler to handle any runtime exceptions that occur, and I would like to handle an exception for displaying the view differently than an exception for saving a record.
Is there a different way of handling what I am trying to do (utilize Springs #ExceptionHandler annotation for specific kinds of request?)
Have you tried using in the same Class? I think that would work. If you wish to use ExceptionHandler then try HandlerExceptionResolver
The reason I am trying to separate the controller for generating the form from the controller saving the form because I am using Springs #ExceptionHandler to handle any runtime exceptions that occur, and I would like to handle an exception for displaying the view differently than an exception for saving a record
I would imagine that your view template engine would throw exceptions of a different type hierarchy than exceptions encountered while saving records in your datastore. It may be easiest to place these methods in the same class, and then just address your #ExceptionResolver concern by mapping exceptions of the view engine's type one way, and DB exceptions another.

Categories