Spring Boot with context path excluding statics - java

I am using Spring Boot as an API and Angular as the frontend for my Application. I build with Maven and so configured the frontend-maven-plugin so it copies all the Angular dist folder into the final jar when building.
What I would like to have is all of my controllers' mapping to have a prefix like '/api' so it becomes '/api/users', but also that my static resources mappings stay what they are, like only '/sign-up' instead of '/api/sign-up'.
So I searched for the server.context-path and server.servlet-path properties but none of them worked. Anyone can help me please ?

#Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class)
.and(HandlerTypePredicate.forBasePackage("com.company.api")));
}
}

Related

Spring app with React UI - how to serve when #EnableWebMvc is applied?

So I have a React app I want to serve from my Spring app (ala this blog). As part of my gradle build task, I run the npm build command and copy the resulting files to /build/resources/main/static. This works fine and I can access my app at mysite.com/index.html, but I want to control who has access more granularly. As such, I applied #EnableWebMvc to my app, but from there, I can't seem to get my API controller to actually serve the view from the build directory. It seems no matter where I put it, it doesn't like serving directly from /build. Any way to make this work?
The handler looks like:
#Controller
class MyController {
#RequestMapping("/")
fun index(): String {
return "index"
}
}
As indicated in the Spring Boot documentation, you do not need - in fact, it is not recommended - to use #EnableWebMvc when using Spring Boot. They state, when describing Spring MVC auto-configuration:
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
And:
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own #Configuration class of type WebMvcConfigurer but without #EnableWebMvc.
In the guide, they continue when describing static content handling:
By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so that you can modify that behavior by adding your own WebMvcConfigurer and overriding the addResourceHandlers method.
In your example, following this advice, you can indicate the static resource handling location with something like (sorry, I am not fluent in Kotlin, forgive for write the example in Java):
#Controller
public class MyController implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static")
;
}
#GetMapping(path = "/")
public String index() {
return "index";
}
}
Please, adapt the paths in addResourceHandlers to your needs.
You can of course place this method in an ad hoc #Configuration.
Having said that, if when you say granular you mean security, the best approach you can take is to configure Spring Security and provide the necessary authorization rules: please, see the relevant documentation.

How to read properties file with IntelliJ

I have a Spring Boot service, and I'm using IntelliJ to run it. I have a file call "application.properties" in my resources folder. I want intelliJ to read that .properties file, how do I do that? the only way I get it to use the properties in .properties file is to add them directly to Environment VM Option.
I tried doing things like
-Dspring.config.location:/src/main/resources/application.properties but that doesnt work.
Folder Structure:
Services
-src
-main
-resources
-application.properties
-target
-pom.xml
#Component
#Configuration
#EnableAutoConfiguration
#PropertySource({ "classpath:application.properties"})
#EntityScan(basePackages = "com.org")
public class AppConfig {
}
In case of Spring Boot, you don't have to pass any option when starting boot application.
When Spring boot application loads, it automatically checks if properties file exists in certain locations including src/main/resources, src/main/resources/config.
If you keep your properties file in these folders, the app automatically picks up the files and register properties. So in your AppConfig you don't need #Component, #EnableAutoConfiguration, #PropertySource, and #EntityScan because #Configuration already includes #Component which enables #Value to work.
I think the problem may arise when you call the property in the constructor of AppConfig because when the class is being constructed the #Value is not injected yet.
If you want to check if the property value is injected by Spring you can make a small test in the application class such as following
#SpringBootApplication
public class ApppropApplication {
#Autowired
private AppConfig appConfig;
public static void main(String[] args) {
SpringApplication.run(ApppropApplication.class, args);
}
#PostConstruct
public void init(){
System.out.println(appConfig.getTestProperty());
}
}
If your problem still exists, it would be great to provide more info (error logs and entire class structure)
Hope this helps! Happy Coding :)
The ideal way for your springboot project to include the properties file would be to import using the annotation "#PropertySource" in your starter class. Please re check your starter class. It should include something like this below
#PropertySource({ "classpath:application-example.properties"})
#EntityScan(basePackages = "com.org")
public class YourProjectLauncher extends SpringBootServletInitializer {
#Value("${your.db.url}")
private transient String dataSourceUrl;
#Value("${your.db.username}")
private transient String userName;
#Value("${your.db.password}")
private transient String password;
#Value("${your.db.driver-class-name}")
private transient String driverClass;
public static void main(String... args) {
SpringApplication.run(YourProjectLauncher.class, args);
}
Let me know if you have already done this and still facing the issue.
Also it would be best if you add the starter class in your question, that way it is easier to analyse the problem you are facing.
Note - If you have already done this, please add more information to the question, will be happy to help.

Spring MVC - accessing a static page through Thymeleaf

I have a Spring Boot web application, using embedded Tomcat + Thymeleaf template engine, and package as an executable JAR file.
Technologies used :
Spring Boot 1.4.2.RELEASE,
Spring 4.3.4.RELEASE,
Thymeleaf 2.1.5.RELEASE,
Tomcat Embed 8.5.6,
Maven 3,
Java 8
I want to access an static file located in ../src/main/resources/templates/mockups/index.html
so I created this controller:
#Controller
public class MockupIndexController {
#RequestMapping("/mockup/index")
public String welcome(Map<String, Object> model) {
return "/mockups/index.html";
}
}
but I got this error:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "/mockups/index.html", template might not exist or might not be accessible by any of the configured Template Resolvers
In spring xml configuration file, map your static files location, please keep static files in different folder
<mvc:resources mapping = "/mockups/**" location = "/src/main/resources/templates/mockups/" />
Changes this line
return "/mockups/index.html";
to
return "redirect:/mockups/index.html";
If you are not using config file then add this class
#Component
class WebConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/mockups/**").addResourceLocations("/src/main/resources/templates/mockups/");
}
}

spring boot request endpoints return 404 [duplicate]

The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.
Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component

Configure ViewResolver with Spring Boot and annotations gives No mapping found for HTTP request with URI error

I'm trying to make "hello world" application with gradle, spring boot and spring mvc with the simplest view resolver and html.
I started from the thymeleaf spring boot example and I just wanted to remove thymeleaf to make a simpler mvc application using pure html and InternalResourceViewResolver. I have a single greeting.html I want to serve which is located at src/main/webapp/WEB-INF. When I run the app I get
No mapping found for HTTP request with URI [/WEB-INF/greeting.html] in DispatcherServlet with name 'dispatcherServlet'
This is a common error and there are a lot of answers on the web but nothing seems to help.
Here is my Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here is my GreetingController.java
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
}
Here is my MvcConfiguration.java
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
}
I run it with gradle bootRun
Here is the repo with the code: https://github.com/driver-pete/spring-mvc-example
Here are some more clues:
Thymeleaf view resolving works fine
InternalResourceViewResolver resolves to the right path
WEB-INF and greeting.html seems to be present in the war file
I do not have jsp or jstl so I do not miss those jars as some might suggest
My hypothesis is that dispatcher servlet somehow get configured to serve on /* instead of / like here and everywhere. However I don't have web.xml so those advices do not apply here. I see a lot of examples how to configure dispatcher servlet programmatically but I want to keep my app at minimum and I suspect that spring boot is supposed to configure it ok since it works fine with thymeleaf.
You only need to enable the default servlet, this is done by adding the following to your MvcConfiguration:
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Essentially what is happening is Spring does not know how to handle the handling of such content natively(could be a jsp say), and to this configuration is the way to tell it to delegate it to the container.
View resolver can also be configured in application.properties file of Spring-Boot web applications, something like below:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
After investigating more I discovered an alternative solution that works without adding configureDefaultServletHandling method. You need to add an embedded tomcat jsp engine to build.gradle:
compile("org.apache.tomcat.embed:tomcat-embed-jasper")
As opposed to configureDefaultServletHandling method this solution works not only with plain html but also with jsp.
All solutions are available at: https://github.com/driver-pete/spring-mvc-example
This solution is available on master.
Biju's solution is on DefaultServletHandling_solution branch.
If you are using spring above 5.0, then use org.springframework.web.servlet.view.InternalResourceViewResolver
instead of
org.springframework.web.servlet.InternalResourceViewResolver
in your bean definition

Categories

Resources