Customize Error pages on AEM Author
AEM comes with a standard error page handling, and error handling in AEM is one of the most basic task. But 99% error handling is done for AEM sites, which we also call published/live pages. In order to handle error for sites there are multiple approaches like ACS commons Error Page Handler or one can handle the error at dispatcher level itself and can leverage dispatcher caching.
But recently i came across below use case.
Use Case:- Customer wants to handle error for out of the box pages in AEM author like asset detail page. For example, User profile is not properly set up for first time login and when user does not have permission to content/dam a 404 page with stack trace is shown to user or an existing user is trying to access a resource that is not present or he/she does not has permission to access it.
Why we should handle error scenarios :-
- We should never present excessive information to user, with stack trace printed on screen , we are exposing our repo structure.
- It never gives good product experience , specifically to a user who is logging in for the first time.
- Presenting a custom error page, provide a better experience and custom user message that states what needs to be done next to access the page.
The customisation that i am going to implement is generic and can be used by both sites and asset implementation. It is not just restricted to this specific use case. In this tutorial we are going to customize out of the box error handler jsp.
Customizing 404.jsp :-
To handle errors AEM provides 404.jsp under libs/sling/servlet/errorhandler. We are going to overlay this script and modify according to our requirements.
Note:- Before making any edits to 404.jsp please understand that 404.jsp
script has been specifically designed to cater to AEM authentication; in particular, to allow for system login in the case of these errors. Therefore, replacement of this script should be done with great care.
Let’s try accessing a page that doesn’t exist in AEM. For example:- http://localhost:4502/content/we-retail/us/en/boy.html
So, let’s get started to fix this issue :-
- Open crx/de and Navigate to
/libs/sling/servlet/errorhandler
. - Right click errorhandler folder and select Overlay Node.
- Make sure to select Match Node Types checkbox.
- Copy the default script 404.jsp from /libs/sling/servlet/errorhandler/ to /apps/sling/servlet/errorhandler/.
- Navigate to
/apps/sling/servlet/errorhandler
and open 404.jsp. - Replace its code with below code. Here we are checking for specific error code and adding our custom logic.
- To display our custom error message it is always preferred to add it in a separate file, so that it is always easy to merge/update this file during upgrades.
<%--
Copyright 1997-2008 Day Management AG
Barfuesserplatz 6, 4001 Basel, Switzerland
All Rights Reserved.
This software is the confidential and proprietary information of
Day Management AG, ("Confidential Information"). You shall not
disclose such Confidential Information and shall use it only in
accordance with the terms of the license agreement you entered into
with Day.
==============================================================================
Generic 404 error handler
Important note:
Since Sling uses the user from the request (depending on the authentication
handler but typically HTTP basic auth) to login to the repository and JCR/CRX
will simply say "resource not found" if the user does not have a right to
access a certain node, everything ends up in this 404 handler, both access
denied ("401", eg. for non-logged in, anonymous users) and really-not-existing
scenarios ("404", eg. logged in, but does not exist in repository).
--%><%
%><%@ page session="false" %><%
%><%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0" %><%
%><sling:defineObjects /><%
%><%@ page import="
org.apache.sling.engine.auth.Authenticator,
org.apache.sling.engine.auth.NoAuthenticationHandlerException,
com.day.cq.wcm.api.WCMMode" %><%!
private boolean isAnonymousUser(HttpServletRequest request) {
return request.getAuthType() == null
|| request.getRemoteUser() == null;
}
private boolean isBrowserRequest(HttpServletRequest request) {
// check if user agent contains "Mozilla" or "Opera"
final String userAgent = request.getHeader("User-Agent");
return userAgent != null
&& (userAgent.indexOf("Mozilla") > -1
|| userAgent.indexOf("Opera") > -1);
}
%><%
boolean isAuthor = WCMMode.fromRequest(request) != WCMMode.DISABLED
&& !sling.getService(SlingSettingsService.class).getRunModes().contains("publish");
// decide whether to redirect to the (wcm) login page, or to send a plain 404
if (isAuthor
&& isAnonymousUser(request)
&& isBrowserRequest(request)) {
Authenticator auth = sling.getService(Authenticator.class);
if (auth != null) {
try {
auth.login(request, response);
// login has been requested, nothing more to do
return;
} catch (NoAuthenticationHandlerException nahe) {
bindings.getLog().warn("Cannot login: No Authentication Handler is willing to authenticate");
}
} else {
bindings.getLog().warn("Cannot login: Missing Authenticator service");
}
}
// get here if authentication should not take place or if
// no Authenticator service is available or if no
// AuthenticationHandler is willing to authenticate
// So we fall back to plain old 404/NOT FOUND
%>
<%-- Custom logic start for displaying 404 error message --%>
<%
Integer scObject1 = (Integer) request.getAttribute("javax.servlet.error.status_code");
int statusCode1 = (scObject1 != null)
? scObject1.intValue()
: HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
if(isAuthor && slingRequest != null && statusCode1 == 404) { %>
<%@include file="/apps/sling/servlet/errorhandler/errormessage.jsp" %>
<% return; }%>
<%-- Custom logic ends for displaying 404 error message --%>
<%@include file="/libs/sling/servlet/errorhandler/default.jsp"%>
- Create a new file by name of errormessage.jsp under /apps/sling/servlet/errorhandler. You can have it at any location and with any custom name.
- Add below line of code to it and save.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Error | Adobe Experience Manager</title>
</head>
<body>
<div>
<h1>ATTENTION <%= statusCode1 %></h1>
<p>
Please consult your application support team.
</p>
</div>
</body>
</html>
That’s it , now we are done. Lets test the changes.
Use Case 1:- When customer user profile is not properly set up or he doesn’t have access to specific asset on author instance.
Use Case 2:- Accessing a page that does not exist. For Example: http://localhost:4502/content/we-retail/us/en/boy.html
Few important points to note:-
- On an author instance, CQ WCM Debug Filter is enabled by default. This always results in the response code 200. The default error handler responds by writing the full stack trace to the response. In order for custom error handler to work on author , we need to disable CQ WCM Debug Filter.
- On a publish instance, CQ WCM Debug Filter is always disabled (even if configured as enabled).
- In AEM as Cloud Service, the CDN serves a generic error page when a 5XX error is received from the backend. In order to allow the actual response of the backend to pass through you need to add the following header to the response: x-aem-error-pass: true.
- This works only for responses coming from AEM or the Apache/Dispatcher layer. Other unexpected errors coming from intermediate infrastructure layers will still display the generic error page.
- As we are customizing the jsp file, its hard to troubleshoot for errors, always keep an eye on error logs they are listed there and can save your efforts.
Leave a Reply