Resolving HTTP-status-codes explicitly to specialized error-pages
authorKai Moritz <kai@juplo.de>
Tue, 21 Jun 2016 08:21:13 +0000 (10:21 +0200)
committerKai Moritz <kai@juplo.de>
Tue, 21 Jun 2016 08:21:13 +0000 (10:21 +0200)
This is needed, for example, to map the status-code 404 to a specialized page.

src/main/java/de/juplo/thymeproxy/Application.java
src/main/java/de/juplo/thymeproxy/ExceptionResolverErrorController.java

index 6b0ad09..6a3d7da 100644 (file)
@@ -15,6 +15,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.core.Ordered;
 import org.springframework.core.env.Environment;
+import org.springframework.http.HttpStatus;
 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
@@ -124,7 +125,7 @@ public class Application extends WebMvcConfigurerAdapter
     mappings.setProperty("TemplateInputException", "templates/404");
 
     resolver.setExceptionMappings(mappings);
-    resolver.setDefaultErrorView("templates/500");
+    resolver.setDefaultErrorView("templates/error");
     resolver.setWarnLogCategory("exception");
     return resolver;
   }
@@ -135,11 +136,15 @@ public class Application extends WebMvcConfigurerAdapter
       ErrorAttributes errorAttributes
       )
   {
-    return new ExceptionResolverErrorController(
-        context,
-        errorAttributes,
-        properties.getError()
-        );
+    ExceptionResolverErrorController controller =
+        new ExceptionResolverErrorController(
+            context,
+            errorAttributes,
+            properties.getError()
+            );
+    controller.addErrorMapping(HttpStatus.NOT_FOUND, "templates/404");
+    controller.setDefaultErrorView("templates/error");
+    return controller;
   }
 
 
index 6315100..7339ddf 100644 (file)
@@ -4,6 +4,7 @@ package de.juplo.thymeproxy;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -23,6 +24,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.core.annotation.AnnotationAwareOrderComparator;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.support.PropertiesLoaderUtils;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
@@ -52,8 +54,8 @@ public class ExceptionResolverErrorController extends BasicErrorController
    */
   private static final String DEFAULT_STRATEGIES_PATH =
       "DispatcherServlet.properties";
-  private static final Properties DEFAULT_STRATEGIES;
 
+  private static final Properties DEFAULT_STRATEGIES;
   static
   {
     // Load default strategy implementations from properties file.
@@ -78,6 +80,12 @@ public class ExceptionResolverErrorController extends BasicErrorController
   }
 
 
+  /** Name of the default-error-view */
+  private String defaultErrorView = "error";
+
+  /** Mapping from HTTP-status-codes to specialized error-pages */
+  private Map<HttpStatus, String> errorMappings = new HashMap<>();
+
   /** List of HandlerExceptionResolvers used by this servlet */
   private List<HandlerExceptionResolver> handlerExceptionResolvers;
 
@@ -135,7 +143,20 @@ public class ExceptionResolverErrorController extends BasicErrorController
       }
     }
 
-    return new ModelAndView("error", model);
+    String viewName = null;
+    Integer code = (Integer)model.get("status");
+    try
+    {
+      HttpStatus status = HttpStatus.valueOf(code);
+      viewName = errorMappings.get(status);
+    }
+    catch(Throwable t)
+    {
+      LOG.warn("cannot map status-code {}: {}", code, t.getMessage());
+    }
+    if (viewName == null)
+      viewName = defaultErrorView;
+    return new ModelAndView(viewName, model);
   }
 
 
@@ -280,6 +301,52 @@ public class ExceptionResolverErrorController extends BasicErrorController
   }
 
 
+  /**
+   * @
+   * @see #addErrorMapping(HttpStatus, String)
+   */
+  public String addErrorMapping(Integer status, String viewName)
+  {
+    if (status == null)
+      throw new IllegalArgumentException("The status must not be null");
+    return addErrorMapping(HttpStatus.valueOf(status), viewName);
+  }
+
+  /**
+   * Adds a mapping from a {@link HttpStatus} to a view.
+   * 
+   * @param status The {@link HttpStatus}, that should be mapped.
+   * @param viewName The name of the view, the status should be mapped to. 
+   * @return The name of the view, the status was previously mapped to, or
+   * <code>null</code>, if the status was not mapped before.
+   */
+  public String addErrorMapping(HttpStatus status, String viewName)
+  {
+    if (!StringUtils.hasText(viewName))
+      throw new IllegalArgumentException("The view-name must not be empty!");
+    if (status == null)
+      throw new IllegalArgumentException("The status must not be null!");
+    return errorMappings.put(status, viewName);
+  }
+
+  /**
+   * Sets mappings from {@link HttpStatus} to specialized error-views.
+   * @param mappings The mappings to set.
+   */
+  public void setErrorMappings(Map<HttpStatus, String> mappings)
+  {
+    errorMappings = mappings;
+  }
+
+  /**
+   * Sets the default error-view for not-mapped status-codes.
+   * @param view The default error-view to set.
+   */
+  public void setDefaultErrorView(String view)
+  {
+    defaultErrorView = view;
+  }
+
   /**
    * Set whether to detect all HandlerExceptionResolver beans in this servlet's
    * context. Otherwise,