I have a vuejs + spring boot app. All was working fine, but suddenly got this issue - requests to files in /js/, /css/, /img/ are returning the index.html content despite having a resource mapping pointing to classpath:/static.
Can't trance the original change which lead to the appearance of this problem. front-end works fine by itself (tried deploying to surge & zeit now), so i suppose the problem is that spring boot ignores the resource mapping.
spring boot v2.1.2
WebMvcConfig:
#Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
String baseApiPath = "/api";
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/**/*.css", "/**/*.html", "/**/*.js", "/**/*.png", "/**/*.ttf")
.setCachePeriod(0)
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("/")
.setCachePeriod(0)
.addResourceLocations("classpath:/static/index.html")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
if (resourcePath.startsWith(baseApiPath) || resourcePath.startsWith(baseApiPath.substring(1))) {
return null;
}
return location.exists() && location.isReadable() ? location : null;
}
});
}
}
in index.html links like this <script src=/js/chunk-vendors.b7114b0e.js></script><script src=/js/app.5c7ddca5.js></script> returning the index.html itself.
Related
servlet not doing redirect, used Spring Boot 2.4.1. Code and comments below.
Class servlet
code
#WebServlet(name = "estore",
urlPatterns = {"/"},
loadOnStartup = 1)
public class EstoreServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
IOException{
// Here logic and after need redirect
response.sendRedirect("/index.html");
}
}
Information from request
code
Session - org.apache.catalina.session.StandardSessionFacade#2e555cdd
Servlet path - /
Server name - localhost
Local name - 0:0:0:0:0:0:0:1
Local addr - 0:0:0:0:0:0:0:1
Get Remote user - null
Get method - GET
Get protocol - HTTP/1.1
Get content type - null
Get server port - 8080
Get sheme - http
Get Request URI - /
Get Context Path -
Spring MVC configuration file
code
#Configuration
#ServletComponentScan(basePackages = "internet_store.web_ui.servlet")
#EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resourceViewResolver = new SpringResourceTemplateResolver();
resourceViewResolver.setPrefix("classpath:/templates/internet_store/");
resourceViewResolver.setSuffix(".html");
resourceViewResolver.setTemplateMode(TemplateMode.HTML);
resourceViewResolver.setCharacterEncoding("UTF-8");
resourceViewResolver.setCheckExistence(false);
return resourceViewResolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/", "classpath:/templates/internet_store/")
.setCachePeriod(320000)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable("estore");
}
}
Project structure
Resources
This code return error in browser ERR_TOO_MANY_REDIRECTS. If in controller set return "redirect:/foo" then redirect not doing too. Example below.
code
#GetMapping(value = "/back_client")
public String backButtonClientFormPressed() {
return "service/service";
}
index.html have controller with method
code
#GetMapping(value = "/index")
public String index(ModelMap modelMap) {
updatePage();
Path resourceDirectory = Paths.get("resources");
modelMap.addAttribute("error", "");
refreshData(modelMap);
return "index";
}
Change service/service to redirect:/index going to error ERR_TOO_MANY_REDIRECTS. Maybe problems with resources path? Thank You for You attention.
[SOLVED]
Need delete response.sendRedirect("/index.html"); from servlet class and add
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
Assume that Project is our POJO class. Following function provides to delete a row from database. It is successfully working with POSTMAN requests.
#RestController
#RequestMapping(value = "/project")
#CrossOrigin
public class ProjectController {
private final ProjectServiceImpl projectServiceImpl;
------------
#DeleteMapping
#RequestMapping("/delete/{id}")
public ResponseEntity<Boolean> deleteProject(#PathVariable Long id) {
boolean result = projectServiceImpl.delete(id);
return ResponseEntity.ok(result);
}
------------
}
But requests from Angular project are rejecting with 403 message. And following message is appearing in console screen.
After some searches. I learned, the application have to answer pre-flight requests with 200. To provide this, following function was added to controller.
#GetMapping
#RequestMapping("/delete/{id:[0-9]+}")
public ResponseEntity.BodyBuilder retreive(#PathVariable Long id) {
return ResponseEntity.ok();
}
I used regex for request mapping because without it Spring Framework throws /project/delete/{id} already mapped with another function. Angular get its 200OK for pre-flight request with this way. But the application response is 406 for delete operation. Angular is sending http://localhost:8080/project/delete/2 url to the application. If I send same link without have a function for CORS. Row has id with 2 will delete successfully. Am I missing something?
Sources:
Why Angular sending OPTIONS message before DELETE
How to add CORS support to Spring Boot application
To implement server side proxy: proxy.conf.json
{
"/project/**": {
"target": "http://localhost:8080",
"secure": false
}
}
modified section in angular.json
"serve": {
"builder": "#angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "issue-management:build",
"proxyConfig": "proxy.conf.json"
},
and Angular project started with ng serve --proxy-config proxy.conf.json but result didn't change. Plus, suggestions in this article applied, again result didn't change.
Your applications are running on two different ports, that causing the CORS issue.
Add the proxy(file proxy.conf.json) in your Angular application.
{
"/project/**": {
"target": "http://localhost:8080",
"secure": false
}
}
and run this ng serve --proxy-config proxy.conf.json
Refference Angular doc
Update:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("http://localhost:4200");
}
};
}
worked, For some reason Angular proxy is not working
If you are using spring security use the following:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults())
...
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
See spring documentation: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors
Global configuration:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Value("${cors.origins.urls}")
public String allowedOrigins;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedOrigins(allowedOrigins.split(","));
}
}
I use Spring Boot and Apache Wicket in a web application. I have to add file upload.
In the below code Wicket's Form component's onSubmit method is fired but "uploads" is empty.
#Override
protected void onSubmit()
{
final List<FileUpload> uploads = fileUploadField.getFileUploads();
if (uploads != null)
{
for (FileUpload upload : uploads)
{
// Create a new file
File newFile = new File(getUploadFolder(), upload.getClientFileName());
// Check new file, delete if it already existed
checkFileExists(newFile);
try
{
// Save to new file
newFile.createNewFile();
upload.writeTo(newFile);
// UploadPage.this.info("saved file: " + upload.getClientFileName());
}
catch (Exception e)
{
throw new IllegalStateException("Unable to write file", e);
}
}
}
}
Spring's configure method
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().antMatchers("/**").permitAll()
.and()
.logout()
.permitAll();
http.headers().frameOptions().disable();
}
I have created a separate application which contains Wicket but not Spring and same upload code works without problem.
I have tried this and did not work.
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
#Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver getMultipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
and when i add below suggestion to Form component's action url via javascript, Wicket's "onSubmit" method is not even triggered
Spring MVC - upload file is blocked by spring security
Edit:
When I watch on the network I see that Spring returns 302 to Wicket's POST request for upload.
Adding this to application.properties file solved the problem.
spring.servlet.multipart.enabled=false
I am trying to load js files in my spring boot application, but when I try to load "/resources/static/js/jsfile.js" using the url http://localhost:8080/js/jsfile.js I am getting a 404 not found error.
I need to prevent this controller from getting called when I try to load something in src/main/resources/static/
#GetMapping("/{username}/{slug}")
public String single(#PathVariable("username") String username, #PathVariable("slug") String slug, Model model) {
model.addAttribute("user", postService.getSinglePost(slug, username));
return "/post/single";
}
After reading other solutions I tried creating this but I'm still getting 404 errors.
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
registry
.addResourceHandler("/css/**")
.addResourceLocations("/css/");
registry
.addResourceHandler("/img/**")
.addResourceLocations("/img/");
registry
.addResourceHandler("/js/**")
.addResourceLocations("/js/");
}
}
I have a single-page web app that's using Backbone.js client routing with pushState. In order to get this to work, I have to tell my server (Java, Spring 3, Tomcat) which URLs should be resolved on the server (actual JSP views, API requets), and which should simply be sent to the index page to be handled by the client. Currently I'm using an InternalResourceViewResolver to simply serve JSP views that match the name of the URL request. Since client-side URLs don't have a view on the server, the server returns a 404.
What is the best way to specify to Spring (or Tomcat) that a few specific URLs (my client-side routes) should all resolve to index.jsp, and anything else should fall through to the InternalResourceViewResolver?
I found that Spring MVC 3 added a tag that does exactly what I need, the mvc:view-controller tag. This got it done for me:
<mvc:view-controller path="/" view-name="index" />
<mvc:view-controller path="/admin" view-name="index" />
<mvc:view-controller path="/volume" view-name="index" />
http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html
In theory, to handle navigation via history.pushState you want to return index.html for unhandled resources. If you look at official documentation for modern web frameworks it's often realised based on 404 status.
In spring you should handle resources in order:
path mapped REST controllers
app static resources
index.html for others
To do this you have at least 4 possible solutions.
Using EmbeddedServletContainerCustomizer and custom 404 handler
#Controller
static class SpaController {
#RequestMapping("resourceNotFound")
public String handle() {
return "forward:/index.html";
}
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/resourceNotFound"));
}
Using custom default request mapping handler
#Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
static class SpaWithHistoryPushStateHandler {
}
static class SpaWithHistoryPushStateHandlerAdapter implements HandlerAdapter {
#Override
public boolean supports(final Object handler) {
return handler instanceof SpaWithHistoryPushStateHandler;
}
#Override
public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
response.getOutputStream().println("default index.html");
return null;
}
#Override
public long getLastModified(final HttpServletRequest request, final Object handler) {
return -1;
}
}
#Bean
public SpaWithHistoryPushStateHandlerAdapter spaWithHistoryPushStateHandlerAdapter() {
return new SpaWithHistoryPushStateHandlerAdapter();
}
#PostConstruct
public void setupDefaultHandler() {
requestMappingHandlerMapping.setDefaultHandler(new SpaWithHistoryPushStateHandler());
}
Using custom ResourceResolver
#Autowired
private ResourceProperties resourceProperties;
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations(resourceProperties.getStaticLocations())
.setCachePeriod(resourceProperties.getCachePeriod())
.resourceChain(resourceProperties.getChain().isCache())
.addResolver(new PathResourceResolver() {
#Override
public Resource resolveResource(final HttpServletRequest request, final String requestPath, final List<? extends Resource> locations, final ResourceResolverChain chain) {
final Resource resource = super.resolveResource(request, requestPath, locations, chain);
if (resource != null) {
return resource;
} else {
return super.resolveResource(request, "/index.html", locations, chain);
}
}
});
}
Using custom ErrorViewResolver
#Bean
public ErrorViewResolver customErrorViewResolver() {
final ModelAndView redirectToIndexHtml = new ModelAndView("forward:/index.html", Collections.emptyMap(), HttpStatus.OK);
return (request, status, model) -> status == HttpStatus.NOT_FOUND ? redirectToIndexHtml : null;
}
Summary
Fourth option looks simplest but as always it depends what you need. You may also want to restric returning index.html only when request expects text/html (which BasicErrorController already do based on "produces" header).
I hope one of this options will help in your case.
I would give a clear scheme to my urls and separate frontend from backend.
Some suggestions:
Route all requests starting by /server to the backend and all others to the frontend.
Setup two different domains, one for the backend, one for the frontend.