Is there a way to server static files in Java Webflux? - java

Hi everyone i am searching now the full day and i do not found a solution.
I could server static file in a mvc spring application without problems but with webflux i do not found a way how i can serve them.
I put in ressource a folder with the name static and in there its a simple html file.
My configuration looks like:
#Configuration
#EnableWebFlux
#CrossOrigin(origins = "*", allowedHeaders = "*")
public class WebConfig implements WebFluxConfigurer {
#Bean
public RouterFunction<ServerResponse> route() {
return RouterFunctions.resources("/", new ClassPathResource("static/"));
}
When i start the application and go to localhost i just received a 404 response.
I also try it with adding:
spring.webflux.static-path-pattern = /**
spring.web.resources.static-locations = classpath:/static/
to the application.properties but i still received the 404 not found.
Even when i added Thymeleaf to my dependencies i still get 404.
Hopefully someone knows what to do.

What i think you are missing is basically to tell on what type (GET) of request you want to serve data.
Here is an old pice of code i found that i have used when i served a react application from a public folder in the resource folder.
When doing a GET against /* we fetch the index.html. If the index is containing javascript that does returning requests they are caught in the second router, serving whatever is in the public folder.
#Configuration
public class HtmlRoutes {
#Bean
public RouterFunction<ServerResponse> htmlRouter(#Value("classpath:/public/index.html") Resource html) {
return route(GET("/*"), request -> ok()
.contentType(MediaType.TEXT_HTML)
.bodyValue(html)
);
}
#Bean
public RouterFunction<ServerResponse> imgRouter() {
return RouterFunctions
.resources("/**", new ClassPathResource("public/"));
}
}

Related

Spring Boot 2.7.5 + Angular 15 as a single war

I'm working on a full-stack app having spring boot v2.7.5 as the backend and Angular v15 as the front end. I use IntelliJ IDEA IDE for development. Locally, spring boot runs on http://localhost:8080 and angular runs on http://localhost:4200. I use Gradle to build the project a single war file and which would be deployed on an external tomcat server.
Following is the project structure:
I have 3 build.gradle files, 1 for frontend , 1 for backend, and 1 for global. When I run the global build.gradle file, it would call call build.gradle from fronend folder which builds angular project and copies all the build files and put them into backend/src/main/resources/static folder. Next, build.gradle from the backend gets called which would build the final war file to be deployed on the external tomcat server.
The reason I'm putting frontend build files (index.html, some .js files) into backend/src/main/resources/static is the fact that Spring Boot Serves static content from that location. more details .
So the static directory looks like this after adding frontend build files:
When I try to access http://localhost:8080, it loads index.html from the static folder.
So far it is good. When I click the login button, internally it calls the backend API and moves to the next page (home page i.e., http://localhost:8080/fe/appInstances).
Now if I refresh the page, it gives me the following 404 Whitelabel Error Page.
I understand that since this is spring-boot as it is looking for a definition of the http://localhost:8080/fe/appInstances API endpoint in the java code.
To fix this, I have created the following IndexController.java class which should redirect all the frontend rest endpoints to index.html which is present in main/resources/static folder.
IndexController.java
#Controller
public class IndexController {
#GetMapping("/")
public String index() {
return "redirect:/index";
}
#GetMapping("/fe/*")
public String anyFrontEndApi() {
return "index";
}
}
But now, I get the following Whitelabel error page about Circular view path [index]: would dispatch back to the current handler URL [/fe/index] again.
I have tried changing #Controller to #RestController and changing the return type to ModelandView or something like this. But irrespective of all, it is still giving me the Whitelabel Error Page about Circular view path...
#RestController
public class IndexController {
#GetMapping("/")
public String index() {
return "redirect:/index";
}
#GetMapping("/fe/*")
public ModelAndView anyFrontEndApi() {
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
}
Am I missing something here? Can someone please suggest me a fix for this?
PS: #justthink addressed this situation here. But I don't know how to do reverse proxy way.
We had this situation of page refresh for Angular and Springboot and we resolved this by adding the below Configuration class extending WebMvcConfigurerAdapter
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
}
So basically, we are telling Springboot that if we have the resource, use the same if not then redirect it to index.html.
Now, to handle the path in Angular, it depends on how you would have written your routes. If the path is available, you show the page, if not, display 404 page.
Hope this helps.
Update 1:
WebMvcConfigurerAdapter is deprecated. If this causes any trouble, then instead of extending the class WebMvcConfigurerAdapter, you can implement WebMvcConfigurer
If you see the whitelabel error says that "this application has no explicit mapping for /error".
That means if no path is matched with the paths that are defined in controller mappings, it forwards the request to "/error" route. So we can override this default behaviour.
Spring provides ErrorController interface to override this functionality
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError() {
return "forward:/";
}
}

Spring Boot SPA URL Rewrite

I'm trying to build an SPA backend (static content server, api) with some additional features/controls that require a flexible URL rewrite/routing/handling. These requirements are proving difficult to achieve together, despite trying the approach in some similar answers I've read through on here.
What I need to do:
Serve static assets (js,images,css,html,etc) from URL path: /assets/
Store these static assets in a filesystem directory and map to the above path
For any static asset request not found return a 404
Expose REST APIs from a set of named URL paths: /api/ and /anotherapi/ etc...
For all other requests outside of these URL paths, serve /index.htm to bootstrap the SPA
So far, I have the following...
For the REST APIs:
#RestController
#RequestMapping(value="/api/**")
public class StateAPIController {
#RequestMapping(value = {"/api/method1"}, method = RequestMethod.POST)
#ResponseBody
public String method1() {
return "method1...";
}
#RequestMapping(value = {"/api/method2"}, method = RequestMethod.POST)
#ResponseBody
public String method2() {
return "method2...";
}
}
(This works fine)
For rendering static files from a specific filesystem location and mapping "/" to "/index.htm":
#Configuration
#EnableWebMvc
public class AssetServerConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/")
.addResourceLocations("file:/some/path/index.htm");
registry
.addResourceHandler("/assets/**")
.addResourceLocations("file:/some/path/assets/");
}
#Bean
public ViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(InternalResourceView.class);
return viewResolver;
}
}
(This works, but not sure if the best way to solve this)
To redirect/forward any other requests (outside of those reserved paths) to "/" (and therefore "/index.htm"):
#ControllerAdvice
#RequestMapping(value="/**")
public class AssetServerController {
#RequestMapping(value = {"/**/{path:[^\\.]*}", "/{path:^(?!/assets/).*}", "/{path:^(?!/api/).*}"}, method = RequestMethod.GET)
public String index() {
return "forward:/";
}
}
(This only partially works... and the main issue I need help with)
So, here, I need to exclude a list of paths (/assets/ & /api/), but this is proving difficult to get right with the regex/AntPathMatcher filter in the RequestMapping, and has both false matches (showing index.htm when it shouldn't) and misses (showing 404s when it should show index.htm).
Due to the above, I also cannot correctly serve 404s when a resource is missing under one of the reserved paths (e.g. assets).
a) what is the best way to approach this? Have I got this completely wrong? Is there a better way?
b) how do I make the regex work, as it doesn't seem to follow normal regex rules, and examples I've seen so far don't achieve my goal...
Answered here: Spring RequestMapping Regex to exclude string
Based on answer here: Spring #RequestMapping "Not Contains" Regex
Pattern that worked for excluding /assets/:
value = {"/{path:(?!.*assets).+}/**"}

How to redirect to default resource file on 404 for particular path

I'm integrating single page application into Spring Boot project. The context of the UI (SPA) is http://localhost:8080/ui/
The context of Spring Boot application itself is http://localhost:8080/. Controllers have different context that has nothing to do with UI context.
There is a case when UI changes browser address line to URL that server does not know about, but does not send request to server. After such thing, if I refresh the page, server responds with 404. However I need to return the default index.html page.
Example: I go to http://localhost:8080/ui/, UI changes this to http://localhost:8080/ui/mainpage. I refresh the page and get 404.
I have found similar question, but I would like to do it a bit differently, then answered there.
I need to return default resource (index.html) when there is a request to http://localhost:8080/ui/**, if request is made to http://localhost:8080/context1/blablabla, I would like to return 404.
After debugging and googling about this I came with next solution:
#Configuration
public static class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/ui/**")
.addResourceLocations("/ui/")
.resourceChain(false)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource resource = super.getResource(resourcePath, location);
return Objects.isNull(resource) ? super.getResource("index.html", location) : resource;
}
});
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ui/").setViewName("index.html");
}
}
The approach here is to add manually PathResourceResolve and override its method getResource, so when resource is null, call for index.html resource. This way I can be sure that I return default page only when request is made to http://localhost:8080/ui/** and all other requests will return 404 as usual.
I do not think that this is the right solution, to me it looks like hack. I thought maybe resource handlers have some config like default resource, but I did not found anything about that.
My question is how to do it properly?
Appreciate any suggestions.

How to configure hot reload in Jhipster?

I am using Jhipster(Angular + Springboot) Application for my existing project.
I managed to create a controller(app.resource) manually apart from the ones already generated by jhiptser(using .jh file) for achieving a file download functionality.
So, when we start the server we usually initiate two servers i.e gradlew and npm start. The second runs on port 9000 which eventually supports hot reload functionality.(front-end development)
So the problem is, I am able to access those endpoints from the server running on standard 8000 port. However, from the port which is a proxy(9000), the method is returning 404.
I tried to clean build the application several times.
NOTE: The #RequestMapping value on the new controller is different then those present already.
Does this have to do something with spring security?
Thanks in advance.
Here is the previous controller:
#RestController
#RequestMapping("/api")
public class FGAppDiagramResource {
#GetMapping(value = "/fg-app-diagram-downloadFile")
public void getImage(String fileName,String folderName, HttpServletResponse
response){
// Some Code
}
}
Here is my New controller:
#RestController
#RequestMapping("/fileDownload")
public class DownloadFileController {
private final Logger log =
LoggerFactory.getLogger(DownloadFileController.class);
public DownloadFileController() {
super();
}
#Autowired
private ApplicationProperties applicationProperties;
#GetMapping(value = "/fg-app-diagram-downloadFile/{fileName}/{folderName}")
public void getImage(#PathVariable String fileName,#PathVariable String folderName, HttpServletResponse response) {
// Some Code
}
}
Your new controller does not use /api so you must add your endpoint URL /fileDownload to proxy configuration of webpack dev server in webpack/webpack.dev.js
proxy: [{
context: [
/* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */
'/api',
'/fileDownload',
You may want to use /api/fileDownload to avoid changing proxy configuration and also because /api is useful for many other aspects like security and also using HTML5 URL routing strategy in Angular to get rid of # in client routes (see https://github.com/jhipster/generator-jhipster/pull/9098).
/api and /management are namespaces to avoid route conflicts, so it is usually wise to use them for your new endpoints.

Spring Boot 2.0.2 MultipartConfigElement not being configured for MultipartFile

I'm using Spring Boot 2.0.2.RELEASE, and not being able to upload files for a REST controller endpoint.
Following this getting starter, it says:
As part of auto-configuring Spring MVC, Spring Boot will create a
MultipartConfigElement bean and make itself ready for file uploads.
So, theoretically, It should work without any additional configurations, but it looks like this MultipartConfigElement is not being configured at all.
I'm getting this warn:
WARN .a.w.r.e.DefaultErrorWebExceptionHandler: Failed to handle request [POST http://localhost:8080/upload]: Response status 400 with reason "Required MultipartFile parameter 'file' is not present"
My Spring application starter is as simple as:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
And my endpoint is:
#RestController
public class MyController {
#PostMapping("/upload")
public String hash(#RequestParam("file") MultipartFile file) {
final String test = file.getContentType();
}
This is the way I'm sending with postman:
I also made sure to unmark any default content type set by postman, with no success.
What possibly am I doing wrong?
First, add this to your properties file
servlet.multipart.enabled=true
servlet.multipart.max-file-size=20M
And create CommonsMultipartResolver bean as
(name = "multipartResolver")
Same question,but I got these files by this way.
You can found these files in this github repository:
gs-uploading-files
All you need to do is just download the zip file of this application,and find the files you need.

Categories

Resources