I'm successfully integrated my Angular2 application with Spring Boot backend following this tutorial, placing the compiled JS resources to a /ui subdirectory. Everything works fine basically, the Angular2 application is accessible through the appname/ui URL.
However, I'd like to know if there is a way to tell Spring to 'pass-through' URLs that children of the /ui path, as currently Spring intercepts every requests targeting /ui/*, preventing the Angular2 Router properly navigating to resources under the /ui path.
Currently, I only have this mapping in one of my controllers:
#RequestMapping(value = "/ui")
public String uiIndex() {
return "/ui/index.html";
}
With this, the interface properly shows up at /ui, but Spring sends me errors and 404s for everything under it, when I address them directly from browser. Router navigation inside the Angular2 app works perfectly though.
EDIT
I'm adding the compiled Angular2 resources from target/ui to static/ui folder with this config (my project uses maven build):
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>*.properties</include>
<include>templates/*.*</include>
</includes>
</resource>
<resource>
<directory>target/ui</directory>
<targetPath>static/ui</targetPath>
</resource>
As to be clear, the only problem is, when I enter an URL in the browser like /ui/home/settings, Spring intercepts the request and throws errors. I can happily navigate to /ui, and then to /home/settings in the Angular context though.
After some trial-and-error, I was finally manage to do what I wanted. Many thanks to #EpicPandaforce 's useful comment and this StackOverflow post
The final solution was to create a #RequestMapping in a #Controller like this:
#RequestMapping(value = "/ui/**/{path:[^\\.]*}")
public String redirectUi() {
return "forward:/ui/index.html";
}
You can add ResourceHandlers to configure static content serving in spring boot.
Example
#Configuration
public class StaticResourceConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/ui").addResourceLocations("file:/path/to/your/angular/files");
}
}
See https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-static-content
Now, Step 5 of the tutorial you mentioned has a cleaner way of doing this, did you skip that step? (src/main/resources/static)
I think that the most simple way to do what you want is using Angular Hash Location Strategy.
import { LocationStrategy, HashLocationStrategy } from '#angular/common';
{ provide: LocationStrategy, useClass: HashLocationStrategy }
I hope to help.
Related
So I have been trying hard to get a simple app to run with populating an HTML file through a controller without using a template engine.
I thought I would take some time to post about my findings related to the subject above. See answer below
Soo the question here is How to access HTML files through a controller in Spring boot without a template engine?
This is a very simplistic answer to a simple issue, this is not meant to scale and does not take into account the entirety of your project
Please keep in mind this is not the norm in a business environment but it was really bothering me how I cannot get a simple HTML to populate from a controller without using a template engine. I got it to work with thymeleaf dependency but I wanted to just get it to work as bare bones as possible. I read a lot of posts with ViewResolver Config classes or things a like but nothing worked. It would conflict with the internal resolvers of Spring boot.
Simple answer is once spring boot is initialized...all your static files must be in resources/static/
Templates folder is specific to use for Thymeleaf, based on my findings and it wont work if you just put it in the resource folders.
With your files in resources/static/ you can access it by going to localhost:8080/yourfilename.html
But if you want to access it through a controller you create a controller with the following:
#Controller
public class IndexController {
#RequestMapping("/")
public String getIndex(){
return " index.html";
}
}
If you want to remove the .html in your return value then you must add the following to your application.properties file
spring.mvc.view.suffix=.html
Then you can use the following
#Controller
public class IndexController {
#RequestMapping("/")
public String getIndex(){
return " index";
}
}
Once again this is a simple research I did on my own just to get it to work.
I'm recently working with microservices, developed as Spring Boot applications (v 2.2) and in my company we're using Keycloak as authorization server.
We chose it because we need complex policies, roles and groups, and we also need the User Managed Authorization (UMA) to share resources between users.
We configured Keycloak with a single realm and many clients (one client per microservice).
Now, I understand that I need to explicitly define Resources within Keycloak and this is fine, but the question is: do I really need to duplicate all of them in my microservice's property file?
All the documentation, examples and tutorials end up with the same thing, that is something like:
keycloak.policy-enforcer-config.enforcement-mode=PERMISSIVE
keycloak.policy-enforcer-config.paths[0].name=Car Resource
keycloak.policy-enforcer-config.paths[0].path=/cars/create
keycloak.policy-enforcer-config.paths[0].scopes[0]=car:create
keycloak.policy-enforcer-config.paths[1].path=/cars/{id}
keycloak.policy-enforcer-config.paths[1].methods[0].method=GET
keycloak.policy-enforcer-config.paths[1].methods[0].scopes[0]=car:view-detail
keycloak.policy-enforcer-config.paths[1].methods[1].method=DELETE
keycloak.policy-enforcer-config.paths[1].methods[1].scopes[0]=car:delete
(this second example fits better our case because it also uses different authorization scopes per http method).
In real life each microservice we're developing has dozens of endpoints and define them one by one seems to me a waste of time and a weakness in the code's robustness: we change an endpoint, we need to reconfigure it in both Keycloak and the application properties.
Is there a way to use some kind of annotation at Controller level? Something like the following pseudo-code:
#RestController
#RequestMapping("/foo")
public class MyController {
#GetMapping
#KeycloakPolicy(scope = "foo:view")
public ResponseEntity<String> foo() {
...
}
#PostMapping
#KeycloakPolicy(scope = "bar:create")
public ResponseEntity<String> bar() {
...
}
}
In the end, I developed my own project that provides auto-configuration capabilities to a spring-boot project that needs to work as a resource server.
The project is released under MIT2 license and it's available on my github:
keycloak-resource-autoconf
In my previous experience:
When using pure servlet, we define servlets so that it will serve requests that match specific urls.
When using struts2, we define a filter so that it will serve requests that match specific urls.
When using springMVC in a traditional xml configuration style, we define a dispatcher servlet so that it will serve requests that match specific urls.
But with spring-boot:
Seems no servlet or filter is defined explicitly. But it still could serve specific urls.
The questions is:
Is it still using servlet? If yes, how it get to serve urls without defining servlet or filter explicitly?
Additional related questions (base on tips from comments):
It seems the implementation of SpringBootServletInitializer will be invoked on deploy, but who is going to invoke it?
As you can see here in details, on startup, while initializing an embedded server (Tomcat by default), Spring Boot creates and registers DispatcherServlet as a servlet.
Spring then, as usual, scans your own classes (including the one you invoke SpringApplication.run() from) and sets corresponding mapping for your controllers, if you have any. For example mapping for /hello here:
#RestController
#EnableAutoConfiguration
public class TestSpring {
#RequestMapping("/hello")
String hello() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(TestSpring.class, args);
}
}
I'm getting very confused over whether you can or can't run spring boot stuff and REST endpoints in one application. At the moment I have them in separate project directories, running the springboot UI one with:
#SpringBootApplication
public class LeagueProjectUiApplication {
public static void main(String[] args) {
SpringApplication.run(LeagueProjectUiApplication.class, args);
}
}
and the REST rest endpoints with:
mvn tomcat7:run
and my jersey and tomcat stuff are declared in my pom.xml
Rest:
#Path("/university")
public class University {
#GET
#Path("/{universitycode}")
#Produces(MediaType.APPLICATION_JSON)
public Response returnSingleSummoner(
#PathParam("universitycode") String universityCode) {
}
What's the best way of running both SpringBoot and REST endpoints at the same time, or am I getting completely confused!
Thanks.
When you say REST endpoints, do you mean Jersey endpoints?
Spring Boot supports Jersey,as you can see, for example, here, so nothing theoretically should stop you for putting everything in one application as long as request paths are different.
I have a Spring boot application and I have two completely independent angular projects.
One project for the end user and another project for the admin.
For business reasons, I am forced to have both of them as two different projects. I want both these projects to be packaged into the same WAR and when the user hits the URL for the end user, the end user angular application should be loaded and when the admin URL is accessed, the admin angular project should be loaded.
I know that Spring boot loads the angular project from /public or /static directories. However, since in my case, I have two different projects, I cant put them under the same folder since both of them have many file names in common including index.html.
So far, I have done the following:
Created two different folders under "public" as "enduser" and
"manage".
Created a controller which has two methods :
#Controller
public class SampleController {
#RequestMapping(method = RequestMethod.GET, produces = "text/html", value
= "/enduser")
public String enduser() {
return "/enduser/index.html";
}
#RequestMapping(method = RequestMethod.GET, produces = "text/html", value
= "/admin")
public String admin() {
return "/admin/index.html";
}
}
However, when I do like this only the index.html file is loaded and the other components are not loaded(by which I mean the entire angular project is not loaded). So the dependent views and styles etc are not loaded.
Does anybody have an idea on how to handle the above scenario? Thanks.
Just posting the solution below it might help somebody else.
I am using two different folder where I am placing the contents of dist folder from angular source code.
I have two different angular based applications admin and public.
Locations in spring app where I am placing the compiled angular code is as:
/src/main/resources/admin-app/*
(admin-index.html and other static
files from admin angluar project)
/src/main/resources/static/*
(index.html and other static files from public angluar project)
Spring boot config
#EnableAutoConfiguration
public class AddCustomLocations {
#Bean
WebMvcConfigurer configurer () {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").
addResourceLocations("classpath:/admin-app/");
}
};
}
That's it now when you will hit http://localhost:8080. It will load the public angular app.
And when you will hit
Now when I am trying to open http://localhost:8080/pages/admin-index.html
It will load the admin angular app.
The magic is all files inside static folder will work by default but if we want to create another folder for placing our static files. We will have to notify the spring boot app.
You can also create a sub folder inside the static folder.
public & admin-app
You will have to hit the url like this
localhost:8080/public/index.html
localhost:8080/public/admin-index.html
You will not need to create the above spring configuration for this.
Resolved it the following way:
Created an MVC controller, added two different methods for each URL pattern.Copied the dist files of each project into a a separate folder under static folder.Added a different tag to each project.This way your angular project wont work on grunt serve.So you will have to set url as empty when you run on grunt serve and set it to a value only when you run for build.