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




To view or add a comment, sign in

More articles by Amit Patkar

  • [Akamai] - Tagging Content

    Challenge Do you feel the need to manage your Akamai cache better? Do you get into en entire Cache Flush after each…

Others also viewed

Explore content categories