Allow browser requetsts to force a raw response
This commit is contained in:
parent
7217458681
commit
9af3fcf02d
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -39,16 +41,19 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
|||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
/**
|
||||
* This interceptor detects when a request is coming from a browser, and automatically
|
||||
* returns a response with syntax highlighted (coloured) HTML for the response instead
|
||||
* of just returning raw XML/JSON.
|
||||
* This interceptor detects when a request is coming from a browser, and automatically returns a response with syntax
|
||||
* highlighted (coloured) HTML for the response instead of just returning raw XML/JSON.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
||||
|
||||
public static final String PARAM_RAW_TRUE = "true";
|
||||
public static final String PARAM_RAW = "_raw";
|
||||
|
||||
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
|
||||
String str = StringEscapeUtils.escapeHtml4(theResultBody);
|
||||
if (str == null || theEncodingEnum == null) {
|
||||
|
@ -161,8 +166,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
|
||||
/*
|
||||
* It's not a browser...
|
||||
|
@ -171,9 +175,9 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
if (highestRankedAcceptValues.contains(Constants.CT_HTML) == false) {
|
||||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* It's an AJAX request, so no HTML
|
||||
* It's an AJAX request, so no HTML
|
||||
*/
|
||||
String requestedWith = theServletRequest.getHeader("X-Requested-With");
|
||||
if (requestedWith != null) {
|
||||
|
@ -186,9 +190,14 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
if (theRequestDetails.getRequestType() != RequestTypeEnum.GET) {
|
||||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
|
||||
String[] rawParamValues = theRequestDetails.getParameters().get(PARAM_RAW);
|
||||
if (rawParamValues != null && rawParamValues.length > 0 && rawParamValues[0].equals(PARAM_RAW_TRUE)) {
|
||||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
streamResponse(theRequestDetails, theServletResponse, theResponseObject);
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -201,43 +210,67 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
p = defaultResponseEncoding.newParser(theRequestDetails.getServer().getFhirContext());
|
||||
RestfulServerUtils.configureResponseParser(theRequestDetails, p);
|
||||
}
|
||||
|
||||
EncodingEnum encoding = p.getEncoding();
|
||||
|
||||
EncodingEnum encoding = p.getEncoding();
|
||||
String encoded = p.encodeResourceToString(resource);
|
||||
|
||||
|
||||
theServletResponse.setContentType(Constants.CT_HTML_WITH_UTF8);
|
||||
|
||||
StringBuilder rawB = new StringBuilder();
|
||||
for (String next : theRequestDetails.getParameters().keySet()) {
|
||||
if (next.equals(PARAM_RAW)) {
|
||||
continue;
|
||||
}
|
||||
for (String nextValue : theRequestDetails.getParameters().get(next)) {
|
||||
if (isBlank(nextValue)) {
|
||||
continue;
|
||||
}
|
||||
if (rawB.length() == 0) {
|
||||
rawB.append('?');
|
||||
}else {
|
||||
rawB.append('&');
|
||||
}
|
||||
rawB.append(UrlUtil.escape(next));
|
||||
rawB.append('=');
|
||||
rawB.append(UrlUtil.escape(nextValue));
|
||||
}
|
||||
}
|
||||
if (rawB.length() == 0) {
|
||||
rawB.append('?');
|
||||
}else {
|
||||
rawB.append('&');
|
||||
}
|
||||
rawB.append(PARAM_RAW).append('=').append(PARAM_RAW_TRUE);
|
||||
|
||||
//@formatter:on
|
||||
String out = "<html lang=\"en\">\n" +
|
||||
" <head>\n" +
|
||||
" <meta charset=\"utf-8\" />\n" +
|
||||
" <style>\n" +
|
||||
".hlQuot {\n" +
|
||||
" color: #88F;\n" +
|
||||
"}\n" +
|
||||
".hlAttr {\n" +
|
||||
" color: #888;\n" +
|
||||
"}\n" +
|
||||
".hlTagName {\n" +
|
||||
" color: #006699;\n" +
|
||||
"}\n" +
|
||||
".hlControl {\n" +
|
||||
" color: #660000;\n" +
|
||||
"}\n" +
|
||||
".hlText {\n" +
|
||||
" color: #000000;\n" +
|
||||
"}\n" +
|
||||
".hlUrlBase {\n" +
|
||||
"}" +
|
||||
" </style>\n" +
|
||||
" </head>\n" +
|
||||
"\n" +
|
||||
" <body>" +
|
||||
"<pre>" + format(encoded, encoding) + "</pre>" +
|
||||
" </body>" +
|
||||
"</html>";
|
||||
//@formatter:off
|
||||
|
||||
String out = "<html lang=\"en\">\n" +
|
||||
" <head>\n" +
|
||||
" <meta charset=\"utf-8\" />\n" +
|
||||
" <style>\n" + ".hlQuot {\n" +
|
||||
" color: #88F;\n" + "}\n" +
|
||||
".hlAttr {\n" +
|
||||
" color: #888;\n" +
|
||||
"}\n" + ".hlTagName {\n" +
|
||||
" color: #006699;\n" + "}\n" +
|
||||
".hlControl {\n" +
|
||||
" color: #660000;\n" +
|
||||
"}\n" + ".hlText {\n" +
|
||||
" color: #000000;\n" +
|
||||
"}\n" +
|
||||
".hlUrlBase {\n" +
|
||||
"}" +
|
||||
" </style>\n" +
|
||||
" </head>\n" +
|
||||
"\n" +
|
||||
" <body>" +
|
||||
"This result is being rendered in HTML for easy viewing. <a href=\"" + rawB.toString() + "\">Click here</a> to disable this.<br/><br/>" +
|
||||
"<pre>" +
|
||||
format(encoded, encoding) +
|
||||
"</pre>" +
|
||||
" </body>" +
|
||||
"</html>";
|
||||
//@formatter:on
|
||||
|
||||
try {
|
||||
theServletResponse.getWriter().append(out);
|
||||
theServletResponse.getWriter().close();
|
||||
|
@ -255,9 +288,9 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
if (!accept.contains(Constants.CT_HTML)) {
|
||||
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* It's an AJAX request, so no HTML
|
||||
* It's an AJAX request, so no HTML
|
||||
*/
|
||||
String requestedWith = theServletRequest.getHeader("X-Requested-With");
|
||||
if (requestedWith != null) {
|
||||
|
@ -270,15 +303,14 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
if (theRequestDetails.getRequestType() != RequestTypeEnum.GET) {
|
||||
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
|
||||
if (theException.getOperationOutcome() == null) {
|
||||
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
|
||||
streamResponse(theRequestDetails, theServletResponse, theException.getOperationOutcome());
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
@ -28,7 +31,6 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
@ -171,6 +173,40 @@ public class ResponseHighlightingInterceptorTest {
|
|||
assertThat(output, stringContainsInOrder("<body>", "<pre>", "\n", "</pre>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightForceRaw() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
@Override
|
||||
public Enumeration<String> answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ArrayEnumeration<String>("text/html,application/xhtml+xml,application/xml;q=0.9");
|
||||
}
|
||||
});
|
||||
|
||||
HttpServletResponse resp = mock(HttpServletResponse.class);
|
||||
StringWriter sw = new StringWriter();
|
||||
when(resp.getWriter()).thenReturn(new PrintWriter(sw));
|
||||
|
||||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_PRETTY, new String[] { Constants.PARAM_PRETTY_VALUE_TRUE });
|
||||
params.put(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML });
|
||||
params.put(ResponseHighlighterInterceptor.PARAM_RAW, new String[] { ResponseHighlighterInterceptor.PARAM_RAW_TRUE });
|
||||
reqDetails.setParameters(params);
|
||||
reqDetails.setServer(new RestfulServer());
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
// true means it decided to not handle the request..
|
||||
assertTrue(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightNormalResponse() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
|
@ -202,6 +238,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
ourLog.info(output);
|
||||
assertThat(output, containsString("<span class='hlTagName'>Patient</span>"));
|
||||
assertThat(output, not(stringContainsInOrder("<body>", "<pre>", "\n", "</pre>")));
|
||||
assertThat(output, containsString("<a href=\"?_raw=true\">"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -172,6 +172,12 @@
|
|||
JPA server can now store Conformance resources, per a request
|
||||
from David Hay
|
||||
</action>
|
||||
<action type="add">
|
||||
ResponseHighlightingInterceptor now skips handling responses if it
|
||||
finds a URL parameter of <![CDATA[<code>_raw=true</code>]]> (in other
|
||||
words, if this parameter is found, the response won't be returned as
|
||||
HTML even if the request is detected as coming from a browser.
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.1" date="2015-07-13">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue