Spring boot whitelable error page customization - spring

Help me to understand the below code. It never redirects to a page called 404.html when an error encounters. I am trying to do a default mapping for /error in spring boot.
NOTE: Not using any templates
#Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(9090);
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
}
};
}
}

Try looking at
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
It describes how you can handle different HTTP errors, including 404.
Here's something I've used (ErrorResponse is my own custom class).
#RestController
#RequestMapping("/error")
public class ErrorController {
#RequestMapping("/403")
#ResponseStatus(HttpStatus.FORBIDDEN)
public ErrorResponse handleForbiddenError() {
return new ErrorResponse("forbidden", HttpStatus.FORBIDDEN);
}
#RequestMapping("/404")
#ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound() {
return new ErrorResponse("not found", HttpStatus.NOT_FOUND);
}
}

Related

Returing Hystrix AsyncResult from Spring Boot Controller

I have the following Spring Boot controller:
#Controller
public class TestController {
#Autowired
private TestService service;
#GetMapping(path="/hello")
public ResponseEntity<String> handleGet() {
return service.getResponse();
}
#GetMapping(path="/hello/hystrix")
public Future<ResponseEntity<String>> handleGetAsync() {
return service.getResponseAsync();
}
#GetMapping(path="/hello/cf")
public Future<ResponseEntity<String>> handleGetCF() {
return service.getResponseCF();
}
}
and service:
#Service
public class TestService {
#HystrixCommand
public ResponseEntity<String> getResponse() {
ResponseEntity<String> response = ResponseEntity.status(HttpStatus.OK).body("Hello");
return response;
}
#HystrixCommand
public Future<ResponseEntity<String>> getResponseAsync() {
return new AsyncResult<ResponseEntity<String>>() {
#Override
public ResponseEntity<String> invoke() {
return getResponse();
}
};
}
public Future<ResponseEntity<String>> getResponseCF() {
return CompletableFuture.supplyAsync(() -> getResponse());
}
}
and application:
#EnableHystrix
#SpringBootApplication
#EnableAsync
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
}
When I hit the /hello/cf endpoint, I get a response "Hello"
When I hit the /hello/hystrix endpoint, I get a 404 error.
Am I able to return an AsyncResult from a controller in this manner? If so, what am I doing wrong?
Thanks.
Your service class needs to return a CompletableFuture.
Also, unless you are using AspectJ, the circuit breaker will not work if the method with #HystrixCommand is called from within the same class.

Custom AbstractEndpoint listening to "/" (root)

I've implemented a starter that configures Swagger the way I like. In addition, I'd like to redirect every call to the app's root URL (e.g. localhost:8080) to /swagger-ui.html.
Therefore, I added an own AbstractEndpoint which is instantiated in the #Configuration class as follows:
#Configuration
#Profile("swagger")
#EnableSwagger2
public class SwaggerConfig {
...
#Bean
public RootEndpoint rootEndpoint() {
return new RootEndpoint();
}
#Bean
#ConditionalOnBean(RootEndpoint.class)
#ConditionalOnEnabledEndpoint("root")
public RootMvcEndpoint rootMvcEndpoint(RootEndpoint rootEndpoint) {
return new RootMvcEndpoint(rootEndpoint);
}
}
The respective classes look like this:
public class RootEndpoint extends AbstractEndpoint<String> {
public RootEndpoint() {
super("root");
}
#Override
public String invoke() {
return ""; // real calls shall be handled by RootMvcEndpoint
}
}
and
public class RootMvcEndpoint extends EndpointMvcAdapter {
public RootMvcEndpoint(RootEndpoint delegate) {
super(delegate);
}
#RequestMapping(method = {RequestMethod.GET}, produces = { "*/*" })
public void redirect(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.sendRedirect("/swagger-ui.html");
}
}
As stated in public RootEndpoint(), the custom Endpoint is bound to /root. Unfortunately, I can't specify super(""); or super("/"); as those values throw an exception (Id must only contains letters, numbers and '_').
How can I achieve having a custom Endpoint listening to the root URL in a starter using #Configuration files to instantiate beans?
I solved it with an easier approach by adding a WebMvcConfigurerAdapter bean in the #Configuration:
#Bean
public WebMvcConfigurerAdapter redirectToSwagger() {
return new WebMvcConfigurerAdapter() {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("").setViewName("redirect:/swagger-ui.html");
}
};
}

vaadin + spring boot: Cannot forward to error page for request

By single view application vaadin 7.7.7, spring-boot 1.5 i check uri fragment https:/tld/#!category-name-1 from user and if the category exist show items and if not
VaadinService.getCurrentResponse().sendError(404, "page not found!");
but i got error after update spring-boot 1.5 and vaadin 7.7.7 (with embeded tomcat):
Cannot forward to error page for request [/vaadinServlet/UIDL/] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false
How can i send http error pages from vaadin to user?
ErrorPageCutomizer.java
#Component
public class ErrorPageCutomizer implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"));
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"));
}
}
RestController.java
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ErrorHandlingController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH + "/404")
public String error404() {
return "<div style='font-weight:bold; margin-top:200px; text-align:center; font-size:160%;'>Page not found...<br>to home</div>";
}
#RequestMapping(value = PATH + "/500")
public String error500() {
return "<div style='font-weight:bold; margin-top:200px; text-align:center; font-size:160%;'>500 Internal server error...</div>";
}
#Override
public String getErrorPath() {
return PATH;
}
}
Soliution was:
#Configuration
public class AppInitializer implements WebApplicationInitializer {
#Bean
public ErrorPageFilter errorPageFilter() {
return new ErrorPageFilter();
}
#Bean
public FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
}
Have you tried the SystemMessagesProvider. In that provider you could define an errorUrl for a variety of errors:
public class YourServlet extends VaadinServlet
{
#Override
protected void servletInitialized() throws ServletException
{
super.servletInitialized();
getService().setSystemMessagesProvider(new SystemMessagesProvider()
{
#Override
public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo)
{
final CustomizedSystemMessages c = new CustomizedSystemMessages();
final String errorUrl = "<url to errorpage>";
c.setSessionExpiredURL(errorUrl);
c.setSessionExpiredNotificationEnabled(false);
c.setAuthenticationErrorURL(errorUrl);
c.setAuthenticationErrorNotificationEnabled(false);
c.setCommunicationErrorURL(errorUrl);
c.setCommunicationErrorNotificationEnabled(false);
c.setCookiesDisabledURL(errorUrl);
c.setCookiesDisabledNotificationEnabled(false);
c.setInternalErrorURL(errorUrl);
c.setInternalErrorNotificationEnabled(false);
c.setSessionExpiredURL(errorUrl);
c.setSessionExpiredNotificationEnabled(false);
return c;
}
});
}

How can I inject a custom factory using hk2?

I'm having a hard time to work with jersey test framework.
I have a root resource.
#Path("sample")
public class SampleResource {
#GET
#Path("path")
#Produces({MediaType.TEXT_PLAIN})
public String readPath() {
return String.valueOf(path);
}
#Inject
private java.nio.file.Path path;
}
I prepared a factory providing the path.
public class SamplePathFactory implements Factory<Path> {
#Override
public Path provide() {
try {
return Files.createTempDirectory(null);
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
}
#Override
public void dispose(final Path instance) {
try {
Files.delete(instance);
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
And a binder.
public class SamplePathBinder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(SamplePathFactory.class).to(Path.class);
}
}
And, finally, my test class.
public class SampleResourceTest extends ContainerPerClassTest {
#Override
protected Application configure() {
final ResourceConfig resourceConfig
= new ResourceConfig(SampleResource.class);
resourceConfig.register(SamplePathBinder.class);
return resourceConfig;
}
}
When I tried to test, I got.
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=Path,parent=SampleResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1916953383)
What did I do wrong?
Your AbstractBinders should be registered as an instance, not as a class. So make the change
resourceConfig.register(new SamplePathBinder());
and it should work

Change main endpoint in Spring Data Rest (usind Spring Boot)

Im building a small application using Spring (Boot, Data, Data Rest).
I have some JpaRepositories that aumotatically are exported as Rest endpoints.
What i want to do is to change the base path from / to /api.
Now to list all people for example i do a GET to http://localhost:8080/people and i want the url to be http://localhost:8080/api/people.
I tried adding this config class but nothing happened (it seems that Spring Boot overrides this config):
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected Class<?>[] getRootConfigClasses()
{
return new Class<?>[] { Application.class};
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class<?>[] { RestExporterRestConfig.class, RepositoryRestMvcConfiguration.class };
}
#Override
protected String[] getServletMappings()
{
return new String[] { "/api/*" };
}
}
My Application.java:
#Configuration
#ComponentScan
#Import(RestExporterRestConfig.class)
#EnableJpaRepositories
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and RestExporterRestConfig:
#Configuration
public class RestExporterRestConfig extends RepositoryRestMvcConfiguration {
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", validator());
}
#Bean
#Qualifier
public DefaultFormattingConversionService defaultConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(StringToDate.INSTANCE);
return conversionService;
}
#Bean
public DomainClassConverter<?> domainClassConverter() {
return new DomainClassConverter<DefaultFormattingConversionService>(defaultConversionService());
}
}
Well i figured it out. SpringWebAppInitializer is not necesary in this case. I just added this code to Application.java:
#Bean
public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean reg = new ServletRegistrationBean(dispatcherServlet);
reg.addUrlMappings("/api/*");
return reg;
}
I think this is the correct way to modify (add, change mappings, etc) servlets using Spring Boot.

Resources