How to redirect to an error page when invalid or unknown URL is requested in Spring Boot display - spring

How to display custom error pages(JSP) when invalid or unknown URL is requested in spring boot. Can any one help me either in spring boot or spring MVC(java configuration).
Example:
error page should be displayed if I request /homee instead of /home.

You have to implement a controller like so:
#Controller
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ServerProperties serverProperties) {
super(new DefaultErrorAttributes(), serverProperties.getError());
}
#Override
public ResponseEntity error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)){
return ResponseEntity.status(status).body(ResponseBean.SERVER_ERROR);
}else if (status.equals(HttpStatus.BAD_REQUEST)){
return ResponseEntity.status(status).body(ResponseBean.BAD_REQUEST);
}
return super.error(request);
}
}

The Solution is
#Controller
public class CustomErrorPage implements ErrorController{
#RequestMapping("/customError")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if(statusCode == HttpStatus.NOT_FOUND.value()) {
return "error";
}
else if(statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error";
}
}
return "error";
}
#Override
public String getErrorPath() {
return null;
}
}
specify server.error.path=/customError in application.properties.Alternative to application.properties is
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
#Configuration
public class CustomErrorPageRegistrar {
#Bean
public ErrorPageRegistrar errorPageRegistrar() {
return new ErrorPageRegistrar() {
public void registerErrorPages(ErrorPageRegistry registry) {
System.out.println("custom error page registered");
registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/customError"));
}
};
}
}

Related

Sticky session Ribbon rule in Zuul always has null request

I am attempting to implement a sticky session load balancer rule in a Zuul proxy service. I am using the code from this example: https://github.com/alejandro-du/vaadin-microservices-demo/blob/master/proxy-server/src/main/java/com/example/StickySessionRule.java
I seem to have everything configured correctly, and the rule is triggering in my debugger, but the call to RequestContext.getCurrentContext().getResponse() always returns null, so the cookie is never found, so the rule never takes effect.
The rest of the Zuul config is working 100%. My traffic is proxied and routed and I can use the app fine, only the sticky session rule is not working.
Is there another step I am missing to get the request wired in to this rule correctly?
My route config:
zuul.routes.appname.path=/appname/**
zuul.routes.appname.sensitiveHeaders=
zuul.routes.appname.stripPrefix=false
zuul.routes.appname.retryable=true
zuul.add-host-header=true
zuul.routes.appname.service-id=APP_NAME
hystrix.command.APP_NAME.execution.isolation.strategy=THREAD
hystrix.command.APP_NAME.execution.isolation.thread.timeoutInMilliseconds=125000
APP_NAME.ribbon.ServerListRefreshInterval=10000
APP_NAME.ribbon.retryableStatusCodes=500
APP_NAME.ribbon.MaxAutoRetries=5
APP_NAME.ribbon.MaxAutoRetriesNextServer=1
APP_NAME.ribbon.OkToRetryOnAllOperations=true
APP_NAME.ribbon.ReadTimeout=5000
APP_NAME.ribbon.ConnectTimeout=5000
APP_NAME.ribbon.EnablePrimeConnections=true
APP_NAME.ribbon.NFLoadBalancerRuleClassName=my.package.name.StickySessionRule
The app:
#EnableZuulProxy
#SpringBootApplication
public class ApplicationGateway {
public static void main(String[] args) {
SpringApplication.run(ApplicationGateway.class, args);
}
#Bean
public LocationRewriteFilter locationRewriteFilter() {
return new LocationRewriteFilter();
}
}
EDIT: As requested, the code:
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.netflix.zuul.context.RequestContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
/**
* #author Alejandro Duarte.
*/
public class StickySessionRule extends ZoneAvoidanceRule {
public static final String COOKIE_NAME_SUFFIX = "-" + StickySessionRule.class.getSimpleName();
#Override
public Server choose(Object key) {
Optional<Cookie> cookie = getCookie(key);
if (cookie.isPresent()) {
Cookie hash = cookie.get();
List<Server> servers = getLoadBalancer().getReachableServers();
Optional<Server> server = servers.stream()
.filter(s -> s.isAlive() && s.isReadyToServe())
.filter(s -> hash.getValue().equals("" + s.hashCode()))
.findFirst();
if (server.isPresent()) {
return server.get();
}
}
return useNewServer(key);
}
private Server useNewServer(Object key) {
Server server = super.choose(key);
HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
if (response != null) {
String cookieName = getCookieName(server);
Cookie newCookie = new Cookie(cookieName, "" + server.hashCode());
newCookie.setPath("/");
response.addCookie(newCookie);
}
return server;
}
private Optional<Cookie> getCookie(Object key) {
HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
if (request != null) {
Server server = super.choose(key);
String cookieName = getCookieName(server);
Cookie[] cookies = request.getCookies();
if (cookies != null) {
return Arrays.stream(cookies)
.filter(c -> c.getName().equals(cookieName))
.findFirst();
}
}
return Optional.empty();
}
private String getCookieName(Server server) {
return server.getMetaInfo().getAppName() + COOKIE_NAME_SUFFIX;
}
}
I think you are missing a PreFilter, like this:
import com.netflix.zuul.context.RequestContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
public class PreFilter extends com.netflix.zuul.ZuulFilter {
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
RequestContext.getCurrentContext().set(FilterConstants.LOAD_BALANCER_KEY, ctx.getRequest());
return null;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER;
}
#Override
public String filterType() {
return "pre";
}
}
Mark as Bean
#Bean
public PreFilter preFilter() {
return new PreFilter();
}
And use it in your rule
#Override
public Server choose(Object key) {
javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) key;
RequestContext not working cause "hystrix.command.APP_NAME.execution.isolation.strategy=THREAD"

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 to set priority in ExceptionHandling via ControllerAdvice

I was implement 2 ControllersAdvice to. handle exception
CommonAdvice and UserAdvice
Common Advice
#ControllerAdvice(annotations = RestController.class)
public class CommonAdvice {
#ExceptionHandler(Exception.class)
public ResponseEntity<ExceptionBean> handleException(Exception e) {
ExceptionBean exception = new ExceptionBean(Causes.ANOTHER_CAUSE);
return new ResponseEntity<ExceptionBean>(exception, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
UserAdvice
#ControllerAdvice(assignableTypes = { requestUserMapper.class })
public class UserAdvice {
#ExceptionHandler(NotUniqueUserLoginException.class)
public ResponseEntity<ExceptionBean> handleAlreadyFound(NotUniqueUserLoginException e) {
System.out.println("this is me : " + Causes.USER_ALREADY_EXIST.toString());
ExceptionBean exception = new ExceptionBean(Causes.USER_ALREADY_EXIST);
return new ResponseEntity<ExceptionBean>(exception, HttpStatus.INTERNAL_SERVER_ERROR);
}
And now, when I throw NotUniqueUserException, this is a CommonAdvice which handle and exception.
I tested and UserAdvice works fine.
There is the way to set priority on this classes ?
#Edit - add Controllel Mapping
#RequestMapping(value = "add", method = RequestMethod.POST)
public ResponseEntity<GT_User> addUser(#RequestBody GT_User newUser) throws NotUniqueUserLoginException, Exception {
if (this.userService.exist(newUser.getLogin())) {
throw new NotUniqueUserLoginException(Causes.USER_ALREADY_EXIST.toString());
} else {
GT_User addesUser = this.userService.addUser(newUser);
return new ResponseEntity<GT_User>(addesUser, HttpStatus.OK);
}
}
To set Higher priority to an ControllerAdvice on add :
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.genealogytree.webapplication.dispatchers.requestUserMapper;
#ControllerAdvice(assignableTypes = { requestUserMapper.class })
#Order(Ordered.HIGHEST_PRECEDENCE)
public class UserAdvice {
...
}
To set Lower priority to an ControolerAdvice on add
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.genealogytree.webapplication.dispatchers.requestUserMapper;
#ControllerAdvice(assignableTypes = { requestUserMapper.class })
#Order(Ordered.LOWEST_PRECEDENCE)
public class CommonAdvice {
...
}

How to set jersey in spring boot 1.4.1-RELEASE to convert dates to ISO 8601?

In Spring boot 1.3.6-RELEASE I had the below class registered to jersey. Every java.util.Date field would be read and returned as ISO8601 format. However, when updating to 1.4.1-RELEASE it now sometimes works and sometimes doesn't. What's the new proper way to enable this?
package com.mypackage;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Date;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.util.ISO8601Utils;
#Provider
public class DateTimeParamConverterProvider implements ParamConverterProvider {
#SuppressWarnings("unchecked")
#Override
public <T> ParamConverter<T> getConverter(Class<T> clazz, Type type, Annotation[] annotations) {
if (type.equals(Date.class)) {
return (ParamConverter<T>) new DateTimeParamConverter();
} else {
return null;
}
}
static class DateTimeParamConverter implements ParamConverter<Date> {
#Override
public java.util.Date fromString(String value) {
if (value == null) {
return null;
}
try {
return ISO8601Utils.parse(value, new ParsePosition(0));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
#Override
public String toString(Date value) {
return ISO8601Utils.format(value);
}
}
}
I register this provider like this:
#Component
#ApplicationPath("/")
public class JerseyConfiguration extends ResourceConfig {
private static final Logger log = Logger.getLogger(JerseyConfiguration.class.getName());
#Autowired
public JerseyConfiguration(LogRequestFilter lrf) {
register(new ObjectMapperContextResolverNonNull());
register(RestServiceImpl.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
register(DateTimeParamConverterProvider.class, 6000);
...
Just define this in your application.properties:
spring.jackson.date-format=com.fasterxml.jackson.databind.util.ISO8601DateFormat

#RestController cannot convert from ResponseEntity<Void>

I've got following code for my REST Controller:
#RequestMapping(value = "foo", method = RequestMethod.GET)
public ResponseEntity<Result> doSomething(#RequestParam int someParam)
{
try
{
final Result result = service.getByParam(someParam);
if (result == null)
{
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else
{
return ResponseEntity.ok(result);
}
} catch (Exception ex)
{
LOG.error("Error blah", ex);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I would like to use ResponseEntity.noContent().build() but Eclipse gives me:
Type mismatch: cannot convert from ResponseEntity to
ResponseEntity
Is there any way to overcome this?
Update:
It is possible to create helper like this:
public class ResponseUtils
{
public static <T> ResponseEntity<T> noContent()
{
return withStatus(HttpStatus.NO_CONTENT);
}
public static <T> ResponseEntity<T> internalServerError()
{
return withStatus(HttpStatus.INTERNAL_SERVER_ERROR);
}
public static <T> ResponseEntity<T> accepted()
{
return withStatus(HttpStatus.ACCEPTED);
}
private static <T> ResponseEntity<T> withStatus(HttpStatus status)
{
return new ResponseEntity<T>(status);
}
}
So I can use it like:
return ResponseUtils.noContent();
But maybe there is built-in functionality for this stuff?
Is this that you want to achieve?
#RequestMapping(value = "foo", method = RequestMethod.GET)
public ResponseEntity<Result> doSomething(#RequestParam int someParam) {
try {
final Result result = service.getByParam(someParam);
if (result == null) {
return ResponseUtils.noContent();
} else {
return new ResponseEntity<Result>(result, null, HttpStatus.ACCEPTED);
}
} catch (Exception ex) {
return ResponseUtils.internalServerError();
}
}
//you forgot to add static keyword in this Utils class
public static class ResponseUtils{
public static <T> ResponseEntity<T> noContent(){
return withStatus(HttpStatus.NO_CONTENT);
}
public static <T> ResponseEntity<T> internalServerError(){
return withStatus(HttpStatus.INTERNAL_SERVER_ERROR);
}
public static <T> ResponseEntity<T> accepted(){
return withStatus(HttpStatus.ACCEPTED);
}
private static <T> ResponseEntity<T> withStatus(HttpStatus status){
return new ResponseEntity<T>(status);
}
}
Check imports, I am using:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.xml.transform.Result;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

Resources