Need help on the basics -
I have integrated Angular and Spring Boot.
I made production build of the Angular app and copied the 6 files in the Spring boot static resource folder.
By default when I hit localhost:8080 index.html is rendered as Spring boot Automatically registers it as welcome page.
Now when i am inside angular i can navigate to different component via ANGULAR ROUTER and the url is also changing.
But when i copy the same URL for example - localhost:8080/myTask and enter it in url address bar it throws 404 resource not found.
Because it hits the Spring controller first and since there is no mapping for that it fails.
In the class where you have extended WebMvcConfigurerAdapter in Spring Boot, inside addViewControllers method, you should do something like this
#Override
public void addViewControllers(final ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/myTask").setViewName("forward:/");
}
for forwarding, all request, you can do registry.addViewController("/**").setViewName("forward:/");
Update Thanks Jonas for the Suggestion. Since WebMvcConfigurerAdapter is deprecated in Spring 5.0, you can implement the above logic by extending WebMvcConfigurer
// the perfect solution(from jhipster)
#Controller
public class ClientForwardController {
#GetMapping(value = "/**/{path:[^\\.]*}")
public String forward() {
return "forward:/";
}
}
If you don't use Spring MVC (for example, you are using Jersey), you can also solve this by using a javax.servlet.Filter:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class AngularRoutingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
if (shouldDispatch(requestURI)) {
request.getRequestDispatcher("/").forward(request, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
private boolean shouldDispatch(String requestURI) {
/* Exclude/Inlclude URLs here */
return !(requestURI.startsWith("/api") || requestURI.equals("/"));
}
}
Related
I'm wondering if there is any pattern or implementation that allows me to enable/disable all the controllers at once of a given spring boot application by using a simple boolean variable that was provided by another feature flags service.
I'm thinking of putting a conditional check in each of the controller paths but it's a really bad way of doing it.
Thanks in advance
You can define custom Filter and register that. Sample Filter code at.
public class RequestAllowDenyFilter implements Filter {
private boolean isAllowed = true;
public RequestAllowDenyFilter(boolean isAllowed) {
this.isAllowed = isAllowed;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isAllowed) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
}
}
}
Then when you register filter you need to pass do you want to allow/deny request.
#Value("${request.enabled:true}")
private boolean isEnabled;
#Bean
public FilterRegistrationBean<RequestAllowDenyFilter> loggingFilter() {
FilterRegistrationBean<RequestAllowDenyFilter> registrationBean
= new FilterRegistrationBean<>();
registrationBean.setFilter(new RequestAllowDenyFilter(isEnabled));
registrationBean.addUrlPatterns("/**");
registrationBean.setOrder(0);
return registrationBean;
}
You need to define request.enabled in application.properties file. Either true/false based on what you want to do.
I have a series of Rest API Controllers in my Spring boot application with Request Mappings that match certain URLs.
I need to change my implementation to always make sure that a specific custom header is in place for all requests. If header is not there I want to fail the request. If it is I want to forward to the appropriate controller which would be the same as my current implementation.
Is there a way to do this in Spring Boot without modifying my existing controllers at all? Could I try to use something like Spring Security, even though my header is not related to security at all?
Thank you.
Web MVC defines an abstraction called "HandlerInterceptor" and its no-op implementation HandlerInterceptorAdapter
So you can register the bean that looks like this:
#Component
public class RequestProcessingTimeInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// check the headers, extract them from request, whatever
return true; // if you want to proceed to controller
return false;// otherwise :)
}
}
This will instruct spring mvc to call the method before the flow gets to the controller.
You can configure a Filter as a #Service.
#Service
#NoArgsConstructor #Log4j2
public class FilterImpl implements Filter {
#Override
public void init(FilterConfig config) throws ServletException { }
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request.getHeader("required-header-name") != null) {
chain.doFilter(request, response);
} else {
log.info("Rejected {}", request);
}
}
#Override
public void destroy() {
}
}
How do I log a JSON or XML request in a database or log file before processing in Spring boot using #RequestBody annotation?
Using which class can I perform this?
Or any link would be helpful.
You can use filter (CommonsRequestLoggingFilter class) approach or you can use below code with custom implementation
#Component
public class AppRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
HttpServletRequest requestCacheWrapperObject = new ContentCachingRequestWrapper(request);
//your implementation
//sample method you can use: requestCacheWrapperObject.getParameterMap(); requestCacheWrapperObject.getContentAsByteArray();
return true;
}
#Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
//your implementation
}
}
#Configuration
public class AppMVCConfig implements WebMvcConfigurer {
#Autowired
private AppRequestInterceptor appRequestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appRequestInterceptor)
.addPathPatterns("/**");
}
}
In order to log request payloads, you can use Spring provided filter CommonsRequestLoggingFilter.
Add following bean into your Spring-Boot config and change log level of org.springframework.web.filter package to DEBUG.
#Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter rlFilter = new CommonsRequestLoggingFilter();
rlFilter.setIncludePayload(true);
return rlFilter;
}
Also, Spring Boot provides Actuator Endpoint (/actuator/httptrace) for HTTP request logging out of the box. Check the below link for more details on this:
Spring Boot Actuator
I have a filter that extends OncePerRequestFilter. When I the management.port=8081 and the server.port=8080 (or any differing ports), my filter is not called on any 8081 Urls.
The filter is only called on 8080 Urls.
How do I make it called on all Urls, including those on 8081?
Filter:
#Order( Ordered.LOWEST_PRECEDENCE )
public class TestFilter extends OncePerRequestFilter
{
public TestFilter()
{
System.out.println( "Started" );
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException
{
System.out.println( "Checked should not filter" );
return false;
}
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
System.out.println( "Filtering" );
// continue the processing
filterChain.doFilter( request, response );
}
}
I add it by:
#Configuration
public class MyConfig
{
#Bean
public TestFilter testFilter()
{
return new TestFilter()
}
}
EDIT:
I tried adding #ManagementContextConfiguration to my config class, but this didn't work either.
Although I was unable to find documentation, it appears the answer is to do all of the following:
Add a class that's annotated with #ManagementContextConfiguration
Put that configuration file outside the component scan (so spring boot's normal auto-config won't find it)
Declare it in META-INF/spring.factories:
META-INF/spring.factories:
before spring-boot-2.0.0.RELEASE:
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=com.packageoutsidescan.MyManagementFilterConfigurationClass
after spring-boot-2.0.0.RELEASE (web subpackage):
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=com.packageoutsidescan.MyManagementFilterConfigurationClass
I am trying to create a custom Spring MVC Controller for a resource that would handle the COPY HTTP method.
#RequestMapping accepts only the following RequestMethod values: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS and TRACE.
Is there any recommended way of handling custom HTTP methods in Spring MVC Controller?
The Servlet specification allows only for GET, HEAD, POST, PUT, DELETE, OPTIONS or TRACE HTTP methods. This can be seen in the Apache Tomcat implementation of the Servlet API.
And this is reflected in the Spring API's RequestMethod enumeration.
You can cheat your way around those by implementing your own DispatcherServlet overriding the service method to allow COPY HTTP method - changing it to POST method, and customize the RequestMappingHandlerAdapter bean to allow it as well.
Something like this, using spring-boot:
#Controller
#EnableAutoConfiguration
#Configuration
public class HttpMethods extends WebMvcConfigurationSupport {
public static class CopyMethodDispatcher extends DispatcherServlet {
private static final long serialVersionUID = 1L;
#Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
if ("COPY".equals(request.getMethod())) {
super.doPost(request, response);
}
else {
super.service(request, response);
}
}
}
public static void main(final String[] args) throws Exception {
SpringApplication.run(HttpMethods.class, args);
}
#RequestMapping("/method")
#ResponseBody
public String customMethod(final HttpServletRequest request) {
return request.getMethod();
}
#Override
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
final RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.requestMappingHandlerAdapter();
requestMappingHandlerAdapter.setSupportedMethods("COPY", "POST", "GET"); // add all methods your controllers need to support
return requestMappingHandlerAdapter;
}
#Bean
DispatcherServlet dispatcherServlet() {
return new CopyMethodDispatcher();
}
}
Now you can invoke the /method endpoint by using COPY HTTP method. Using curl this would be:
curl -v -X COPY http://localhost:8080/method