Configure Spring MVC with AngularJS - spring-mvc

I would like to be able to use Spring MVC as REST server and AngularJS on client side.
I have several urls for REST :
/rest/products
/rest/products/{id}
And i have several urls for the UI :
/shop/products
/shop/products/{id}
Since it is AngularJS which do the trick on client side, i just want to be able to redirect all default ui urls (not the rest ones) to the index.html file used by AngularJS.
So, in Spring MVC configuration, i would like to be able to do something like that :
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "com.mypackage.web")
public class WebAppConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/**").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
With that, i want to delegate all the UI urls handling to AngularJS.
I also want that if the user writes a bad url in the browser he would be redirected by Spring MVC on the index.html file and it will be AngularJS which will do the redirect on the error ui page. I have seen several projects on the web with a single index.html file, but no one handle this error case.
I have been struggling a lot of time trying to do this trick but i can't find a solution.
So my question is : how can i do that ? and more generally, am i wrong with this Spring MVC-AngularJS wanted configuration ?
Very important : I use Spring MVC 3.2 and Tomcat 7.34 without web.xml (full Servlet 3.0)
Thanks a lot.

Maybe it is possible to solve it via $routeProvider:
$routeProvider.when('/redirect/:redirectParams', {templateUrl: 'partials/homePartial', controller: redirectController});
$routeProvider.when('/home', {templateUrl: 'partials/homePartial', controller: homeController});
$routeProvider.when('/someRoute', {templateUrl: 'partials/somePartial', controller: someController});
$routeProvider.when('/error/', {templateUrl: 'partials/errorPartial', controller: errorController});
$routeProvider.when('/error/:errorMessage', {templateUrl: 'partials/errorPartial', controller: errorController});
$routeProvider.otherwise({redirectTo: '/error'})
Just let the $routeProvider redirect to an error page when the route was not found.
Edit:
I've added a redirectController in the above example. This controller will read the $routeParams, in this case $routeParams.redirectParams and use $location to change the route to /error.
Spring just redirects to http://host:port/index.html/#/redirect/error=blabla. You could and should probably finetune this and support multiple routeParams.
In Spring you would have basically three request mappings:
REST request mapping
index.html request mapping
other urls request mapping
To redirect all other requests:
#RequestMapping(value = "{path}", method = RequestMethod.GET)
public String redirect(#PathVariable String path) {
String route = null;
if (path.equals("/") || path.startsWith("/index.html")) {
// load index.html
} else {
route = "redirect:/index.html/#/redirect/" + path;
}
return route;
}

I think you can write an Interceptor which allows some known URLs like /rest/, /resources/, /webjars/* and for any other URLs redirect to index.html.

Try using urlrewritefilter
This should do the work for you. You'll have to configure/add it in your web.xml to every url would be handled and you can manipulate it to redirect to the index.html

I know its a bit late but in case someone run into the same trouble here's the URL filter sample put in your classpath urlwrite.xml
You can set redirection of certain urls you want to filter.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<!-- Configuration file for UrlRewriteFilter http://www.tuckey.org/urlrewrite/ -->
<urlrewrite>
<rule>
<note>
The rule means that requests to /test/status/ will be redirected
to
/rewrite-status
the url will be rewritten.
</note>
<from>/index.ipxs/directory</from>
<to type="redirect">/index.ipxs/marketplace</to>
</rule>
<rule>
<from>/index.ipxs/faq</from>
<to type="redirect">/index.ipxs/marketplace</to>
</rule>
<rule>
<from>/index.ipxs/marketplace</from>
<to type="redirect">/iPlexus/index.ipxs</to>
</rule>
</urlrewrite>

Related

StrictHttpFirewall in spring security 4.2 vs spring MVC #MatrixVariable

Having upgraded to spring security 4.2.4 I discovered that StrictHttpFirewall is now the default.
Unfortunately it doesn't play well with spring MVC #MatrixVariable since ";" are not allowed anymore.
How to get around that?
Example:
#GetMapping(path = "/{param}")
public void example(#PathVariable String param,
#MatrixVariable Map<String, String> matrix) {
//...
}
This could be called like this:
mockMvc.perform(get("/someparam;key=value"))
And the matrix map would be populated.
Now spring security blocks it.
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:140)
I could use a custom HttpFirewall that would allow semicolons.
Is there a way to use #MatrixVariable without using forbidden characters?
BTW: the javadoc is incorrect https://docs.spring.io/autorepo/docs/spring-security/4.2.x/apidocs/index.html?org/springframework/security/web/firewall/StrictHttpFirewall.html
Since:
5.0.1
I guess it was backported?
You can dilute the default spring security firewall using your custom defined instance of StrictHttpFirewall (at your own risk)
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
And then use this custom firewall bean in WebSecurity (Spring boot does not need this change)
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// #formatter:off
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
...
}
That shall work with Spring Security 4.2.4+, but of-course that brings some risks!
As mentioned by Крис in a comment if you prefer to use a XML approach, you can add the following part to your securityContext.xml (or whatever your spring-security related xml-config is called):
<bean id="allowSemicolonHttpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall">
<property name="allowSemicolon" value="true"/>
</bean>
<security:http-firewall ref="allowSemicolonHttpFirewall"/>
The <bean> part defines a new StrictHttpFirewall bean with the id allowSemicolonHttpFirewall which is then set as default http-firewall in the <security> tag by referencing the id.
I used combination of following two
https://stackoverflow.com/a/48636757/6780127
https://stackoverflow.com/a/30539991/6780127
First one resolved the
The request was rejected because the URL contained a potentially malicious String ";"
Second one Resolved the
Spring MVC Missing matrix variable
As I am using Spring Security with Spring Web I had to do both And the issue is now Resolved.
I found using #MatrixVariable Following Pattern is useful. First in Url {num} has to be mentioned to use it as #MatrixVariable
#RequestMapping(method = RequestMethod.GET,value = "/test{num}")
#ResponseBody
public ResponseEntity<String> getDetail(#MatrixVariable String num){
return new ResponseEntity<>("test"+num, HttpStatus.OK);
}

MVC: Serve file with http header

I am trying to follow this guide and store files in a sub-folder.
However, the OneSignal guide asks to serve these files with additional HTTP header Service-Worker-Allowed: /. How do I do that in Asp.Net MVC?
You can use in your controller's action :
this.Response.Headers.Add("Service-Worker-Allowed","/");
Hope this help :-)
Edit :
A better way to do this is to create an action filter to automatically add this header :
public class WorkerAllowedAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Headers.Add("Service-Worker-Allowed", "/");
base.OnActionExecuted(filterContext);
}
}
And you can put it on your action (or your controller) :
public partial class HomeController
{
[WorkerAllowed]
public ActionResult Index()
{
return View();
}
}
No matter what I tried, I could not get the scope allowed even though I could see it in the response header. I figured out a solution based on this article: https://weblog.west-wind.com/posts/2015/Nov/13/Serving-URLs-with-File-Extensions-in-an-ASPNET-MVC-Application
Basically, we are making the service worker js file appear that it 'lives' in the root directory to avoid scope issues. You can leave your service worker js file in whatever directory you want (probably /Scripts). Then you set up an IIS rewrite rule in web.config (or in IIS) that re-routes any request that includes the service worker file name to an action (be sure you specify the exact path to the action). The action then returns the file as type javascript/application.
<system.webServer>
<rewrite>
<rules>
<rule name="Service worker fix">
<match url="pwa-sw.js"/>
<action type="Rewrite" url="/home/serviceworker"/>
</rule>
</rules>
</rewrite>
</system.webServer>
Then the controller
[AllowAnonymous]
[Route("serviceworker")]
public ActionResult serviceworker()
{
return File("/Scripts/pwa-sw.js", "application/javascript");
}

Dynamic Routing with Web API

I have a WebAPI controller with a Get method as follows:
public class MyController : ApiController
{
public ActionResult Get(string id)
{
//do some stuff
}
}
The challenge is that I am attempting to implement WebDAV using Web API. What this means is that as a user browses down a folder structure the URL will change to something like:
/api/MyController/ParentFolder1/ChildFolder1/item1.txt
Is there a way to route that action to MyController.Get and extract out the path so that I get:
ParentFolder1/ChildFolder1/item1.txt
Thanks!
"Dynamic" route is not a problem. Simply use wildcard:
config.Routes.MapHttpRoute(
name: "NavApi",
routeTemplate: "api/my/{*id}",
defaults: new { controller = "my" }
);
This route should be added before default one.
Problem is that you want to end URL with file extension. It will be interpreted as static request to .txt file.
In IIS7+ you can work around that by adding line in web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
Don't forget that if you use MyController, then route segment is just "my"
Use the NuGet package "AttributeRouting Web API". You can specify specific routes for each action, including dynamic parameters.
I was just dealing with this so try it out, and come back if you need more help.

How to prevent static resources from being handled by front controller servlet which is mapped on /*

I have a servlet which acts as a front controller.
#WebServlet("/*")
However, this also handles CSS and image files. How can I prevent this?
You have 2 options:
Use a more specific URL pattern such as /app/* or *.do and then let all your page requests match this URL pattern. See also Design Patterns web based applications
The same as 1, but you want to hide the servlet mapping from the request URL; you should then put all static resources in a common folder such as /static or /resources and create a filter which checks if the request URL doesn't match it and then forward to the servlet. Here's an example which assumes that your controller servlet is a #WebServlet("/app/*") and that the filter is a #WebFilter("/*") and that all your static resources are in /resources folder.
HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());
if (path.startsWith("/resources/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + path).forward(request, response); // Goes to your controller.
}
See also How to access static resources when mapping a global front controller servlet on /*.
I know this is an old question and I guess #BalusC 's answer probably works fine. But I couldn't modify the URL for the JSF app am working on, so I simply just check for the path and return if it is to static resources:
String path = request.getRequestURI().substring(request.getContextPath().length());
if (path.contains("/resources/")) {
return;
}
This works fine for me.

Spring WebFlow: POST from flow to MVC Controller

I have MVC Controller as below and mapped /home to that controller. To redirect to /home from flow i use externalRedirect:contextRelative:/home in view attribute. Is possible to pass some data to /home in POST ?
MVC Controller
#Controller
public class MainController {
#RequestMapping(value="/home", method=RequestMethod.POST)
public String index(#RequestParam String data) {
return "index";
}
}
Flow
<end-state id="home" view="externalRedirect:contextRelative:/home" />
No.
When you are specifying externalRedirect: Spring Webflow is going to set a redirect code and Location header on your response which simply instructs the browser to perform a GET request for the specified location. You can include query parameters appended to this location but not POST data.
For example:
<end-state id="home" view="externalRedirect:contextRelative:/home?foo=bar" />
Also note that you can include ${expressions} in this string that will be evaluated against the request context, according to the XSD.

Resources