/
Read JSON Requests with Stripes 1.5
Read JSON Requests with Stripes 1.5
Dave
Owned by Dave
Last updated: May 26, 2017
Outdated Code
In Stripes 1.7 there is a much better solution to all of this using @RestActionBean. Here's the RESTful Calculator Example:
I used Stripes 1.5 to communicate with an angular 1.5 client which posts with a JSON body by wrapping the HTTPServletRequest.
It uses Jackson to read the request body and stores each JSON property as an HTTP request param, with the property's value as a JSON string.
This allowed us to again use Jackson in our ActionBeans to populate our model objects.
Angular post Expand source
var params = { startDate: asIso8601String(new Date(2017, 0, 1)), endDate: asIso8601String(new Date(2018, 0, 1)) } $http.post(restAPI.stats, { "params": params });
JsonRequestInterceptor Expand source
package com.example.util; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import com.example.util.JsonRequest; import net.sourceforge.stripes.action.ActionBeanContext; import net.sourceforge.stripes.action.Resolution; import net.sourceforge.stripes.controller.ExecutionContext; import net.sourceforge.stripes.controller.Interceptor; import net.sourceforge.stripes.controller.Intercepts; import net.sourceforge.stripes.controller.LifecycleStage; /** * Wrap JSON requests in this wrapper so that Strings can access the JSON contents via request parameters. */ @Intercepts(LifecycleStage.ActionBeanResolution) public class JsonRequestInterceptor implements Interceptor { @Override public Resolution intercept(ExecutionContext executionCtx) throws Exception { ActionBeanContext ctx = executionCtx.getActionBeanContext(); HttpServletRequest request = ctx.getRequest(); String contentType = request.getHeader("content-type"); if (StringUtils.contains(contentType, MediaType.APPLICATION_JSON.toString())) { // wrap the request ctx.setRequest(new JsonRequest(ctx.getRequest())); } return executionCtx.proceed(); } }
JsonRequest Expand source
package com.example.util; import java.io.BufferedReader; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.lang3.ArrayUtils; import org.springframework.http.MediaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; /** * Used to wrap an {@link HttpServletRequest} with a {@link MediaType#APPLICATION_JSON} header so that the body of the request can be read as a JSON object and so that Stripes can still have access to * request parameters. */ public class JsonRequest extends HttpServletRequestWrapper { private Map<String, String[]> parameters = new HashMap<String, String[]>(); public JsonRequest(HttpServletRequest request) throws Exception { super(request); buildParameters(request); } /** * Read the body of the request as JSON then map the root field names as parameter keys. */ private void buildParameters(HttpServletRequest request) throws Exception { StringBuilder builder = new StringBuilder(); String line; BufferedReader reader = request.getReader(); while ((line = reader.readLine()) != null) { builder.append(line); } String body = builder.toString(); ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readValue(body, JsonNode.class); Iterator<String> fieldNames = root.fieldNames(); while (fieldNames.hasNext()) { String key = fieldNames.next(); JsonNode value = root.get(key); parameters.put(key, new String[] { value.toString() }); } } @Override public String getParameter(String name) { String value = null; Map<String, String[]> mergedParams = getParameterMap(); String[] values = mergedParams.get(name); if (ArrayUtils.getLength(values) > 0) { value = values[0]; } return value; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> parentParameters = super.getParameterMap(); Map<String, String[]> mergedParams = new HashMap<String, String[]>(parentParameters.size() + parameters.size()); mergedParams.putAll(parentParameters); mergedParams.putAll(parameters); return mergedParams; } @Override public String[] getParameterValues(String name) { Map<String, String[]> mergedParams = getParameterMap(); return mergedParams.get(name); } }
Example ActionBean
StatsWS Expand source
package com.example.stats; import java.util.Date; import org.apache.commons.lang3.StringUtils; import com.example.stats.StatsService; import com.example.util.JsonTypeConverter; import com.example.util.BaseWS; import com.example.util.JsonResolution; import net.sourceforge.stripes.action.DefaultHandler; import net.sourceforge.stripes.action.Resolution; import net.sourceforge.stripes.action.UrlBinding; import net.sourceforge.stripes.integration.spring.SpringBean; import net.sourceforge.stripes.validation.SimpleError; import net.sourceforge.stripes.validation.Validate; import net.sourceforge.stripes.validation.ValidationErrors; import net.sourceforge.stripes.validation.ValidationMethod; /** * Stats web service. * * Loads global stats, and if a date range is provided loads the interval stats as well. */ @UrlBinding("/ws/stats") public class StatsWS extends BaseWS { private String START_DATE_FIELD_NAME = "params.startDate"; private String END_DATE_FIELD_NAME = "params.endDate"; @SpringBean private StatsService statsService; private StatsParamsRequest params; private Date startDate; // populated during validation private Date endDate; // populated during validation @Validate(required = true, converter = JsonTypeConverter.class) public void setParams(StatsParamsRequest params) { this.params = params; } @DefaultHandler public Resolution reply() { Stats global = statsService.loadGlobalStats(); Stats interval = null; if (startDate != null && endDate != null) { interval = statsService.loadIntervalStats(startDate, endDate); } StatsReply reply = new StatsReply(); reply.setGlobal(global); reply.setInterval(interval); return new JsonResolution(reply); } @ValidationMethod public void validateDates(ValidationErrors errors) { if (params == null) { errors.add("params", new SimpleError("params is null")); } boolean hasStart = !StringUtils.isEmpty(params.getStartDate()); boolean hasEnd = !StringUtils.isEmpty(params.getEndDate()); if (hasStart && hasEnd) { startDate = parseIso8601Date(params.getStartDate(), START_DATE_FIELD_NAME, errors); endDate = parseIso8601Date(params.getEndDate(), END_DATE_FIELD_NAME, errors); } else if (hasStart ^ hasEnd) { // both dates can be null, but not just one of them errors.add(START_DATE_FIELD_NAME, new SimpleError(StatsService.MSG_START_END_DATES)); } } }
JsonTypeConverter Expand source
package com.example.util; import java.io.IOException; import java.util.Collection; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; import com.fasterxml.jackson.databind.ObjectMapper; import net.sourceforge.stripes.validation.TypeConverter; @SuppressWarnings("rawtypes") public class JsonTypeConverter implements TypeConverter { @Override public void setLocale(Locale locale) { // nothing to do } @SuppressWarnings("unchecked") @Override public Object convert(String string, Class type, Collection clctn) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue(string, type); } catch (IOException ex) { Logger.getLogger(JsonTypeConverter.class.getName()).log(Level.SEVERE, null, ex); } return null; } }
How to respond with JSON
JsonResolution Expand source
package com.example.util; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.apache.commons.logging.LogFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import com.example.util.LogUtil; import net.sourceforge.stripes.action.StreamingResolution; /** * The default response to a web service which {@link JsonReply replys} with {@link StreamingResolution streaming} JSON using a Jackson {@link ObjectMapper}. */ public class JsonResolution extends AbstractJsonResolution { private static final LogUtil LOG = LogUtil.getInstance(LogFactory.getLog(JsonResolution.class)); private final JsonReply reply; public JsonResolution(JsonReply reply) { super(); Validate.notNull(reply); this.reply = reply; } @Override public void stream(HttpServletResponse response) { Validate.notNull(response); LOG.trace("Replying with: ", reply.getClass().getSimpleName(), " : ", reply); ObjectMapper mapper = new ObjectMapper(); if (reply instanceof EmptyReply) { mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); } try { if (reply instanceof FilterableJsonReply) { // ignore specific fields String[] nonFilteredFields = ((FilterableJsonReply) reply).getNonFilteredFields(); FilterProvider filters = new SimpleFilterProvider().addFilter("filter", SimpleBeanPropertyFilter.filterOutAllExcept(nonFilteredFields)); mapper.writer(filters).writeValue(response.getOutputStream(), reply); } else { // write full content mapper.writeValue(response.getOutputStream(), reply); } } catch (IOException e) { LOG.error(e); } } }
AbstractJsonResolution Expand source
package com.example.util; import javax.servlet.http.HttpServletResponse; import net.sourceforge.stripes.action.StreamingResolution; /** * An abstract base {@link StreamingResolution streaming} JSON response to a web service. */ public abstract class AbstractJsonResolution extends StreamingResolution { protected static final String DEFAULT_CHARSET = "UTF-8"; protected static final String JSON_MEDIA_TYPE = "application/json"; protected AbstractJsonResolution() { super(JSON_MEDIA_TYPE); setCharacterEncoding(DEFAULT_CHARSET); } @Override protected abstract void stream(HttpServletResponse response); }
, multiple selections available,
Related content
RESTful Calculator Example
RESTful Calculator Example
More like this
RESTful Action Beans
RESTful Action Beans
More like this
Quick Start Guide
Quick Start Guide
More like this
Sample Application
Sample Application
Read with this
Best Practices
Best Practices
More like this
Configuration Reference
Configuration Reference
More like this