Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions api/src/org/labkey/api/action/ApiJsonWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,21 @@ public void writeProperty(String name, Object value) throws IOException
writeObject(value);
}

/** Let subclasses have a chance to do special handling on String values in the response */
protected void writeString(String value) throws IOException
{
jg.writeString(value);
}

@Override
protected void writeObject(Object value) throws IOException
{
ensureNotClosed();
if (value instanceof String || value instanceof Number || value instanceof Boolean || value == null)
if (value instanceof String s)
{
writeString(s);
}
else if (value instanceof Number || value instanceof Boolean || value == null)
{
jg.writeObject(value);
}
Expand Down Expand Up @@ -224,9 +234,9 @@ else if (value instanceof JSONArray jsonArray) // TODO: replace the upstream cre
jg.writeEndArray();
}
}
else if (value instanceof Date)
else if (value instanceof Date d)
{
jg.writeString(DateUtil.formatJsonDateTime((Date) value));
writeString(DateUtil.formatJsonDateTime(d));
}
// Always use Jackson serialization for SimpleResponse, Issue 47216
else if (isSerializeViaJacksonAnnotations() || value instanceof SimpleResponse<?>)
Expand All @@ -239,7 +249,7 @@ else if (value == JSONObject.NULL)
}
else
{
jg.writeString(value.toString());
writeString(value.toString());
}
}

Expand Down
100 changes: 70 additions & 30 deletions api/src/org/labkey/api/action/ExtFormResponseWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,18 @@
import org.labkey.api.query.PropertyValidationError;
import org.labkey.api.query.ValidationError;
import org.labkey.api.query.ValidationException;
import org.labkey.api.util.MimeMap;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;

/*
* User: Dave
* Date: Sep 3, 2008
* Time: 11:03:32 AM
*/

/**
* This writer extends ApiJsonWriter by writing validation errors in the format
* that Ext forms require.
Expand Down Expand Up @@ -68,27 +64,26 @@
*/
public class ExtFormResponseWriter extends ApiJsonWriter
{
boolean sendHtmlJsonResponse = false;
boolean startResponse = true;
public ExtFormResponseWriter(HttpServletResponse response) throws IOException
private Writer _encodingWriter;
private boolean _closed;

public ExtFormResponseWriter(HttpServletRequest request, HttpServletResponse response) throws IOException
{
super(response);
boolean sendHtml = !"XMLHttpRequest".equals(request.getHeader("X-Requested-With")) &&
request instanceof MultipartHttpServletRequest;
super(response, sendHtml ? MimeMap.MimeType.HTML.getContentType() : CONTENT_TYPE_JSON);
setErrorResponseStatus(HttpServletResponse.SC_OK);
}

public ExtFormResponseWriter(HttpServletRequest request, HttpServletResponse response) throws IOException
private boolean isHtml()
{
this(response);
if (!"XMLHttpRequest".equals(request.getHeader("X-Requested-With")) && (request instanceof MultipartHttpServletRequest))
sendHtmlJsonResponse = true;
response.setContentType(sendHtmlJsonResponse ? "text/html" : CONTENT_TYPE_JSON);
return getResponse().getContentType().startsWith(MimeMap.MimeType.HTML.getContentType());
}

public ExtFormResponseWriter(HttpServletRequest request, HttpServletResponse response, String contentTypeOverride) throws IOException
{
this(request, response);
if (!sendHtmlJsonResponse && null != contentTypeOverride)
if (!isHtml() && null != contentTypeOverride)
response.setContentType(contentTypeOverride);
}

Expand Down Expand Up @@ -121,8 +116,8 @@ public void toJSON(JSONObject jsonErrors, ValidationError error)
{
String msg = error.getMessage();
String key = "_form";
if (error instanceof PropertyValidationError)
key = ((PropertyValidationError)error).getProperty();
if (error instanceof PropertyValidationError pve)
key = pve.getProperty();
if (jsonErrors.has(key))
msg = jsonErrors.get(key) + "; " + msg;
jsonErrors.put(key, msg);
Expand All @@ -139,8 +134,8 @@ public void writeResponse(Errors errors) throws IOException
if (message == null)
message = msg;
String key = "_form";
if (error instanceof FieldError)
key = ((FieldError)error).getField();
if (error instanceof FieldError fieldError)
key = fieldError.getField();
if (jsonErrors.has(key))
msg = jsonErrors.get(key) + "; " + msg;
jsonErrors.put(key, msg);
Expand All @@ -161,24 +156,69 @@ public void writeResponse(Errors errors) throws IOException
}
}

@Override
public void close() throws IOException
{
super.close();
if (isHtml() && !_closed)
{
_encodingWriter.flush();
Writer w = super.getWriter();
if (w != null)
{
w.flush();
}
}
_closed = true;
}

@Override
protected Writer getWriter()
{
Writer w = super.getWriter();
if (null == w)
return null;
if (sendHtmlJsonResponse && startResponse)
if (isHtml() && _encodingWriter == null)
{
startResponse = false;
try
{
w.write("<html><body><textarea>");
}
catch (IOException x)
_encodingWriter = new HtmlEncodingWriter(w);
}
return _encodingWriter != null ? _encodingWriter : w;
}

/** Wraps a Writer to HTML-encode all output, so JSON can be safely embedded inside a &lt;textarea&gt; element. */
private static class HtmlEncodingWriter extends FilterWriter
{
HtmlEncodingWriter(Writer out)
{
super(out);
}

@Override
public void write(int c) throws IOException
{
switch (c)
{

case '&' -> out.write("&amp;");
case '<' -> out.write("&lt;");
case '>' -> out.write("&gt;");
case '"' -> out.write("&quot;");
case '\'' -> out.write("&#39;");
default -> out.write(c);
}
}
return w;

@Override
public void write(char[] cbuf, int off, int len) throws IOException
{
for (int i = off; i < off + len; i++)
write(cbuf[i]);
}

@Override
public void write(String str, int off, int len) throws IOException
{
for (int i = off; i < off + len; i++)
write(str.charAt(i));
}
}
}