Reputation: 3807
I want to replace exisiting json
response(In some condition) with the new one using filters. What am trying to do is read existing response (JSON
) from filter. Modify it with new values and write back to response. But the result shows both the response.
That is , what i have read from response and what i added newly. But i need to replace the old response with new. Code added below.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
{
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
chain.doFilter(request,new HttpServletResponseWrapper((HttpServletResponse)response) {
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new DelegatingServletOutputStream(new TeeOutputStream(super.getOutputStream(), ps)
);
}
@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(new DelegatingServletOutputStream (new TeeOutputStream(super.getOutputStream(), ps))
);
}
});
/* get existing response as string*/
String respopn=baos.toString();
JSONObject json=new JSONObject(respopn);
JSONObject dMap=new JSONObject(json.get("dataMap"));
dMap.put("new", "newValue");
json.put("dataMap", dMap); // Modified the old datamap with new json
JsonMapper jsonMap=new JsonMapper();
jsonMap.setJson(json);
String str=jsonMap.getJson();
byte[] responseToSend = restResponseBytes(jsonMap);
response.getOutputStream().write(responseToSend); // write to response only the new one
}
catch(Exception e)
{
e.printStackTrace();
}
}
private byte[] restResponseBytes(Object response) throws IOException {
String serialized = new ObjectMapper().writeValueAsString(response);
return serialized.getBytes();
}
Upvotes: 2
Views: 5432
Reputation: 724
Just coding as below to return custom response body with error code
final ObjectMapper mapper = new ObjectMapper();
final ObjectNode root = mapper.createObjectNode();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
// set return custom response
ErrorRepsonse error = ErrorRepsonse.builder()
.status("400")
.results(List.of(
ErrorRepsonse.Result.builder()
.errorCode("999")
.build()
))
.build();
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write(mapper.writeValueAsString(error));
return; // important!!!
Upvotes: 0
Reputation: 1177
Maybe this could also be considered. So first create a response wrapper. (add the content-type if needed or else you could leave as it is. Mine had a xhml content-type and that prompted me to change the content-type)
Next create wrappers for the printwriter and ServletOutputStream. The wrapper for servlet OutputStream is optional but that depends on you need. Here are the implementations
ByteArrayServletStream.java
public class ByteArrayServletStream extends ServletOutputStream {
ByteArrayOutputStream baos;
ByteArrayServletStream(ByteArrayOutputStream baos) {
this.baos = baos;
}
@Override
public void write(int param) throws IOException {
baos.write(param);
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setWriteListener(WriteListener listener) {
// TODO Auto-generated method stub
}
}
ByteArrayPrinter.java
/**
* IMplemented own Printer as the new wrapper
*
*/
public class ByteArrayPrinter {
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private PrintWriter pw = new PrintWriter(baos);
private ServletOutputStream sos = new ByteArrayServletStream(baos);
public PrintWriter getWriter() {
return pw;
}
public ServletOutputStream getStream() {
return sos;
}
byte[] toByteArray() {
return baos.toByteArray();
}
}
SwaggerFilter.java
public class SwaggerFilter implements Filter {
final String APPLICATION_XHTML = "application/xhtml";
final String XML_ELEMENT_START = "<Json>";
final String XML_ELEMENT_END = "</Json>";
@Override
public void init(FilterConfig config) throws ServletException {
}
/**
* Filter to remove the extra JSON element and render it
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
ByteArrayPrinter pw = new ByteArrayPrinter();
// Create a wrapper
HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
@Override
public void setContentType(final String type) {
super.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
}
@Override
public PrintWriter getWriter() {
return pw.getWriter();
}
// set the outputstream content type to JSON
@Override
public ServletOutputStream getOutputStream() throws IOException {
ServletResponse response = this.getResponse();
String ct = (response != null) ? response.getContentType() : null;
if (ct != null && ct.contains(APPLICATION_XHTML)) {
response.setContentType(ct + AppConstants.CONSTANT_COMMA + MediaType.APPLICATION_JSON_UTF8_VALUE);
}
return pw.getStream();
}
};
chain.doFilter(httpRequest, wrappedResp);
byte[] bytes = pw.toByteArray();
String respBody = new String(bytes);
if (respBody.startsWith(XML_ELEMENT_START)) {
// Instead of using substring made use of stream to identify any occurence of <Json> xml element
List<String> xmlStringList = Stream.of(respBody).filter((s1) -> s1.contains(XML_ELEMENT_START))
// filter the string. Split it by mapping to new arraylist by space
.map((stringBeforeSplit) -> Arrays.asList(stringBeforeSplit.split(AppConstants.CONSTANT_SPACE)))
// create a new stream of array list strings
.flatMap((stringArrayAfterSplit) -> {
StringBuffer concatenateStringStream = new StringBuffer();
stringArrayAfterSplit.forEach(item -> {
concatenateStringStream.append(item);
});
// remove the <JSON> xml element and return the values
return Stream
.of(concatenateStringStream.toString().trim()
.replace(XML_ELEMENT_START, AppConstants.CONSTANT_NO_SPACE)
.replace(XML_ELEMENT_END, AppConstants.CONSTANT_NO_SPACE));
// collect it as a new list of strings with the xmlelement - <JSON> removed
}).collect(Collectors.toList());
// Join the list to make it one
String finalString = String.join(AppConstants.CONSTANT_NO_SPACE, xmlStringList);
// write to the outputstream with JSON mediatype
response.getOutputStream().write(finalString.getBytes());
} else {
response.getOutputStream().write(bytes);
}
}
@Override
public void destroy() {
}
}
Upvotes: 0
Reputation: 66
I feel below code snippet should work.The problem with above is it is getting appended to the existing data instead of rewriting. We need to create a copy with where the data is stored and copy after manipulating to the original ServletResponse. Hope below code snippet solve your issue.
below is the main do filter method-
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletResponseWrapperCopier capturingResponseWrapper = new ServletResponseWrapperCopier(
(HttpServletResponse) response);
chain.doFilter(request, capturingResponseWrapper);
try{
String respopn = capturingResponseWrapper.getCaptureAsString();
JSONObject json=new JSONObject(respopn);
JSONObject dMap=new JSONObject(json.get("dataMap"));
dMap.put("new", "newValue");
json.put("dataMap", dMap); // Modified the old datamap with new json
JsonMapper jsonMap=new JsonMapper();
jsonMap.setJson(json);
String str=jsonMap.getJson();
response.getOutputStream().write(str.getBytes());
} catch(Exception e){
log.error("");
}
}
Below class is used to copy the ServletResponse in the above code snippet
public class ServletResponseWrapperCopier extends HttpServletResponseWrapper{
private final ByteArrayOutputStream capture;
private ServletOutputStream output;
private PrintWriter writer;
public ServletResponseWrapperCopier(HttpServletResponse response) {
super(response);
capture = new ByteArrayOutputStream(response.getBufferSize());
}
@Override
public ServletOutputStream getOutputStream() {
if (writer != null) {
throw new IllegalStateException(
"getWriter() has already been called on this response.");
}
if (output == null) {
output = new ServletOutputStream() {
@Override
public void write(int b) throws IOException {
capture.write(b);
}
@Override
public void flush() throws IOException {
capture.flush();
}
@Override
public void close() throws IOException {
capture.close();
}
};
}
return output;
}
@Override
public PrintWriter getWriter() throws IOException {
if (output != null) {
throw new IllegalStateException(
"getOutputStream() has already been called on this response.");
}
if (writer == null) {
writer = new PrintWriter(new OutputStreamWriter(capture,
getCharacterEncoding()));
}
return writer;
}
public byte[] getCaptureAsBytes() throws IOException {
if (writer != null) {
writer.close();
} else if (output != null) {
output.close();
}
return capture.toByteArray();
}
public String getCaptureAsString() throws IOException {
return new String(getCaptureAsBytes(), getCharacterEncoding());
}
}
Upvotes: 5