With Spring MVC, I know how you set the RequestMapping in every controller and method/action.
But what if I wanted this to be configurable, so for example I the following controllers:
BlogController
- with methods for listing blogs entries, single entry, new, update, etc.
ArticleController
- with methods for listing articles entries, single entry, new, update, etc.
Now in my application, the administrator can setup 2 blogs for the webiste, and 1 article section so the urls would be like:
www.example.com/article_section1/ - uses ArticleController
www.example.com/blog1/ - uses BlogController
www.example.com/blog2/ - uses BlogController
Maybe after a while the administrator wants another article section, so they just configure that with a new section like:
www.example.com/article_section2/
This has to work dynamically/on-the-fly without having to restart the application of course.
My question is only concerned with how I will handle url mappings to my controllers.
How would this be possible with Spring MVC?
I only know how to map urls to controllers using #RequestMapping("/helloWorld") at the controller or method level, but this makes the url mappings fixed and not configurable like how I want it.
Update:
I will be storing the paths in the database, and with the mapping to the type of controller so like:
path controller
/article_section1/ article
/blog1/ blog
/blog2/ blog
..
With the above information, how could I dispatch the request to the correct controller?
Again, not looking to reload/redeploy, and I realize this will require more work but its in the spec :)
Would this sort of URL mapping work for you?
www.example.com/blog/1/
www.example.com/blog/2/
If yes, then that's easy: Spring 3 supports path variables: http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-ann-requestmapping-advanced
Alternatively, you can create a generic request mapping and your own sub-dispatcher that reads a config file, but I think that's probably more work than it's worth.
Truly changing the request mappings at runtime might be hard (and not really recommended, since small errors can easily occur). If you still wish to do it, perhaps JRebel, and more specificly LiveRebel can be interesting for live redeployment of code and configuration.
Otherwise, like other posts suggested, RequestMappings supports wildcards, the limits of this should be clear after a quick read of the official documentation.
Try using with #RequestMapping wild cards as below:
#RequestMapping(value="/article_section*/"}
public void getArticle(....){
//TODO implementation
}
#RequestMapping(value="/blog*/"}
public void getBlog(....){
//TODO implementation
}
Hope this helps!!!
Also another solution might be to create a custom annotation that holds the already defined path on the #RequestMapping and also the new one to apply, let's say #ApiRestController.
Then, before the Spring context loads, the #Controller classes can be changed to have their annotation values changed at runtime by the new one (with the desired path). By doing this, Spring will load the enhanced request mapping and not the default one.
Created a small project to exemplify this for someone that needs this in the future https://gitlab.com/jdiasamaro/spring-api-rest-controllers.
Hope it helps.
Cheers.
doesn't this work?
#RequestMapping("/helloWorld*")
Related
I have a spring boot application with the following context path:
server.servlet.context-path:/api
I need to write a rest controller that's mapped to
http://localhost:8080/logout
instead of http://localhost:8080/api/logout
Is there a way to achieve this? changing the "server.servelt.context-path" value is not an option.
this is what I tried and didn't work:
#GetMapping(value="../signout"){
public void logout(){
}
Nero, you say you can't change the "server.servlet.context-path" value. I bet you say this because you don't want to break the API, but I think you can manage to change this without breaking the API. Set the context-path to blank, which is permitted. Then in your application change the "api" mapping, which I assume is currently "/", to "api".
Change server.servlet.context-path:/api to server.servlet.context-path:/ or maybe server.servlet.context-path: (no slash). (Supposedly this is the default so you might just remove this entry altogether.)
Somewhere in your application change #RequestMapping("/") to #RequestMapping("/api").
Now you can also have #GetMapping(value="/signout") and you will have resources at http://localhost:8080/logout and http://localhost:8080/api.
I don't know what mapping annotations you happen to be using, but hopefully this is clear enough.
It may not be possible within that application to go outside its context root. Maybe you can create a separate Rest service app for that particular url and take it from there.
I'm using a pretty vanilla spring-boot-starter-data-rest setup and enabled the PATCH method. All is working, but I have a security concern and wonder what's the recommended way of mitigating it.
The problem is that PATCH paths allow reachable entities to be updated from a different endpoint. So, suppose I have a comments endpoint and an article endpoint. Each comment has a to-one association with its article. A user that has permission to edit a comment could then do something like this:
PATCH http://some.domain.foo/api/comments/1234
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/article/title", "value": "foobar2" }
]
and thereby change the title of the article !!
Clearly this ain't good.
In this case, for other parts of the API the association to the "article" needs to be traversable. But it must be read-only.
So... how do I accomplish this in Spring?
Intercept the request?
Implement a handler method?
Write my own Controller from scratch ?
Thanks!
Seems that current implementation on spring-data-rest converts paths to SpEL to apply values directly on beans. See PatchOperation (v2.5.x).
Consider these options:
Instead of json-patch use json-merge PATCH request to send partial updates (with "application/json" or "application/merge-patch+json" content type). This will respect #JsonIgnore and other Jackson annotations and also treat associations differently.
You can disable "json-patch+json" completely, for example by adding a security filter
You can always create your custom json-patch implementation, if you still need it
Use application-level joining not relying on JPA, i.e. only exposing IDs of the linked entities and providing custom links in your ResourceProcessor.
Additionally, if you're using JPA and Comment.article is annotated with #ManyToOne make sure that there's no cascading on association. Even if the article object is modified with patch it won't be saved together with the comment.
There's been a recent fix in Spring Data Rest:
https://github.com/spring-projects/spring-data-rest/issues/2177
The commit that resolves this issue is:
https://github.com/spring-projects/spring-data-rest/commit/5d0687d1a1bb9a84264ecb4cd088907837c382d3
From a quick read this seems to be checking that when applying a JSON patch to an entity a check is done with Jackson that the path should be accessible (read/write).
This should prevent users from being able to specify paths in the JSON patch that aren't normally exposed through POST/GET requests that are mapped directly onto entities through Jackson. I think that if you had marked the article as readable from the comment and the title attribute as writeable then the new code should allow it. Or that's I think what org.springframework.data.rest.webmvc.json.patch.JsonPointerMapping#verify is trying to do.
So if you marked the article on the comment as not being readable through Jackson (#JsonIgnore on the getter) then JSON patch shouldn't allow the title of the article to be set through a comment. The association would still exist, just not be exposed through JSON serialization, I'm not sure if this would cause problems for your application.
The change is released in Spring Data Rest 4.0.0 which is part of Spring Data 2022.0.1 which is in Spring Boot 3.0.2.
https://github.com/spring-projects/spring-data-rest/releases/tag/4.0.0
https://github.com/spring-projects/spring-data-bom/releases/tag/2022.0.1
https://github.com/spring-projects/spring-boot/releases/tag/v3.0.2
I am having some trouble understanding this. Can someone help me better understand this?
MVC
Model --> Java- Spring Framework
View ---> templating language(JSP velocity) & Javascript
DB --> SQL
Q-1)
Now, When I open a particular page, I can't visualize the flow. I've read about DAO, controller , service etc and I understand them individually but I am really confused when I club all together what's the order of execution? Whats the flow exactly ? Is it that first the view is loaded then it sends JS request to fetch the necessary data from backend and then the controller and service are invoked and the DAO queries the db? Then how does the API come into picture? DAO deals with the API?
Q-2)
Why do we need xyz.properties? I have removed a module from my page. If I remove a particular js file(related to that module) from the scripts.properties, then ideally that js should not get executed at all right? Then still why would I see the api call to fetch the data related to that module? I don't see the module but I sure see the api call. Why is that?
DB doesn't enter in MVC model. And you're forgetting a principal element in your analysis: the Controller. The flow goes like this:
Client performs a request to an URL
The application server gets the URL and passes the handling to the web application.
The web application using Spring MVC will handle the URL processing to the Controller: DispatchServlet, which is a Servlet.
The DispatchServlet will try handle the URL. If there's an URL mapping, then it will pass it to the class (mapped in the spring.xml config or decorated with #Controller annotation).
This controller (which in fact is part of the model) will handle the request. It will call services, daos, etc (Model) and return the necessary data to complete the response to the DispatchServlet.
The DispatchServlet will finish the request handling and, in the end, will generate the results e.g. a text/json response, or it will forward to a JSP file (View).
For question two, I never have used such scripts.properties file, so I don't know what you're talking about. Usage of a properties file is to store application properties that should not change until an application redeploy. They have 3 main advantages:
They can be easily manipulated by human users. It's no rocket science to add, edit or remove values.
Since it is a plain text, it's easier to version using a version control system like SVN, Git or another of your preference.
It provides a faster access since it is usually in the same disk as the application, so there's no much time penalty when accessing to its contents compared to a database configuration. But since it is in disk, it still has a disadvantage against RAM access only.
In simple layman's term, MVC explained in pictorial form
(inputing data) (data related part) (display rendering)
-request mapping -classes -JSP (Script,CSS,HTML)
-request param -interface -velocity
Controller ------------->Model--------------->View
||
\/
(data processing logic) (access to Databse)
-optimization -JDBC
-business logic -SQL
Service--------------------->DAO
I want to call a method on a Filter object after it has been added according to the web.xml definition. All I got is a WebApplicationContext object (let's call it: wac).
I'm able to add new Filter objects via: wac.getServletContext().addFilter("otherfilter", otherFilter);
Also, I can test successfully for its existence via: wac.getServletContext().getFilterRegistration("myfilter")
But how may I access (and possibly modify) Filter objects which have been added before?
I'm not sure how to do it exactly as you want, but this problem is usually solved using different approach.
You can declare your Filter as a bean in your application context and then register a DelegatingFilterProxy in web.xml to delegate filtering to your filter.
In this case your filter will be a regular Spring bean, and you'll be able to access it like any other bean.
The Servlet API does not provide any mechanism to directly access a Filter instance once it has been added to a ServletContext. The best you are going to get with the Servlet API is the FilterRegistration interface you have already found which lets you modify the same set of configuration options as you can via web.xml.
Depending on exactly what you want to do, you might be able to code your way around this problem using init parameters but that is never going to be a particularly clean solution. I'd go with the DelegatingFilterProxy solution suggested by axtavt.
In Spring 2.0, is there a quick way to have a list of the current servlet context's mapped URLs from inside a Controller method?
I'm writing a method called getMappedUrls(), and the purpose of this method is to show what urls are mapped in the current servlet context.
This method is to be used to generate an small online doc of available services.
Thanks!
[Edit]
Kent Lai's answer works for me:
final Map<Object, AbstractUrlHandlerMapping> beans =
getApplicationContext().getBeansOfType(AbstractUrlHandlerMapping.class);
for (final AbstractUrlHandlerMapping bean : beans.values())
{
final Map<String, Object> mapping = bean.getHandlerMap();
for(final String url : mapping.keySet())
urls.add(url);
}
This sounds like an interesting idea, and so I went to dig inside the Spring Web MVC source code to find out if there is a way.
First I turned on debug for my spring framework logging (log4j), and I noticed mappings are done in my org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping class (since I am using that).
Digging furthur, I noticed that all handler mappings are added to a handler map, which you can obtain from
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerMap
Assuming you are using a handler that derives from it.
Next, I find out that DispatcherServlet is using the HandlerMapping, which AbstractUrlHandlerMapping (and AbstractHandlerMapping) implements, to resolve the url (well obviously they have to use it). And it turns out that DispatcherServlet holds another list of handlerMappings
org.springframework.web.servlet.DispatcherServlet#handlerMappings
The bad news here is, well it is a protected class instance variable.
So my best recommendation (if this can be considered best) that you can do to find out all the mapped url is
Get hold of an instance of ApplicationContext.
Find out all beans of at least type AbstractUrlHandlerMapping (since anything furthur up does not tell you any mapped url).
Iterate the list and call getHandlerMap and get all the keySet, which is the mapped url.
Note that this is what I dig out from my usage of #RequestMapping annotations. I am unsure how different it is if you are mapping urls in your spring configuration files.
The servlet spec hides this information from the servlet code. You can only get the context path used for the current request. If you can get to the web.xml, you would be able to infer the mappings from there, but that's a bit of a hack. You could also have each servlet register itself upon startup.