Accessing AEM Page with HTTP POST
Have you ever felt frustrated, when you want to do a POST instead of GET on an existing AEM Page, I recently encountered a scenario, with which an external integration was POSTing to an AEM page? We always had the option of using a Servlet, or embed a FORM within the page, both of which would have required additional effort and the cost of manageability. The requirement is very simple, convert POST to a GET request.
Problem Statement
Consider the following URL, which displays a product
http://www.acme.com/content/acme/products/myproduct.html?color=red&size=xl
Now, consider an external payment gatewould like to POST back on this page, after the transaction is completed, it will be passing certain parameters such as isSuccess=true, representing a successful transaction, along with the existing parameters color and size
i.e
POST Parameters
isSuccess=true
color=red
size=xl
Servlet and Sling Request Processor to Rescue
Servlet with a specific selector .p2g.html
I chose the selector p2g (Standing for Post to Get), my idea is to pass .p2g. as a selector to any existing page, along with all the parameters sent in the POST, the use SlingRequestProcessor to generate the HTML response, and finally write the response back to the browser
Example
POST URL
http://www.acme.com/content/acme/products/myproduct.p2g.html?color=red&size=xl
P2G Servlet
package com.theadobearchitect.examples;
import com.day.cq.contentsync.handler.util.RequestResponseFactory;
import com.day.cq.wcm.api.WCMMode;
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.engine.SlingRequestProcessor;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
@Component(service=Servlet.class,
property={
"name=" + "The Adobe Architect Examples - P2GServlet",
Constants.SERVICE_DESCRIPTION + "= P2GServlet",
"sling.servlet.methods=" + HttpConstants.METHOD_POST,
"sling.servlet.resourceTypes="+ "sling/servlet/default",
"sling.servlet.selectors="+ "p2g",
"sling.servlet.extensions="+ "html"
})
/**
* Convert Post To Get, we can whitelist and black list paths later on
*/
public class P2GServlet extends SlingAllMethodsServlet {
private static final long serialVersionUID = 1L;
final Logger LOG = LoggerFactory.getLogger(P2GServlet.class);
@Reference
private transient RequestResponseFactory requestResponseFactory;
@Reference
private transient SlingRequestProcessor slingRequestProcessor;
@Override
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
//LOG.info(request.getResource().getPath());
boolean doNotContinue = false;
/**
* Don't Continue if we find :formstart as a parameter in post
* :formstart is used by Forms Framework, I don't want to disturb any OOB functionality
* Also if .error.html is invovked we want to skip processing that too as its for client error handling
*/
if (!StringUtils.isEmpty(request.getParameter(":formstart")) || (!StringUtils.isEmpty(request.getRequestPathInfo().getSelectorString()) && request.getRequestPathInfo().getSelectorString().equalsIgnoreCase("error"))) {
doNotContinue = true;
}
//DECIDE on this, this should not even happen, lets restrict Servlet with resourceType
if (doNotContinue) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
LOG.warn("{},This should not have happened. Path either contains a selector .html or the form post contains variable :formstart",request.getResource() != null ? request.getResource().getPath(): "PATH NOT FOUND");
return ;
}
String extension = request.getRequestPathInfo().getExtension();
//LOG.info("Extension:{}",extension);
ByteArrayOutputStream out = new ByteArrayOutputStream();
HttpServletRequest httpServletRequest = requestResponseFactory.createRequest("GET",request.getResource().getPath() + "."+ (StringUtils.isEmpty(extension) ? "html" : extension),request.getParameterMap());
WCMMode.DISABLED.toRequest(httpServletRequest);
HttpServletResponse httpServletResponse = requestResponseFactory.createResponse(out);
httpServletRequest.setAttribute("convertingPOSTtoGet","1");
if (request.getParameterNames() != null) {
Enumeration<String> enmParamNames = request.getParameterNames();
while (enmParamNames.hasMoreElements()) {
String paramName = enmParamNames.nextElement();
if (request.getParameter(paramName) != null) {
httpServletRequest.setAttribute(paramName,request.getParameter(paramName));
}
}
}
slingRequestProcessor.processRequest(httpServletRequest,httpServletResponse,request.getResourceResolver());
String html = out.toString();
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(html);
}
}
Code Base
https://github.com/theadobearchitect/aem-example-post-2-get