Unescape Read ID and Version IDs
This commit is contained in:
parent
9828d27eec
commit
46b6979125
|
@ -30,6 +30,7 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -51,6 +52,7 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
@ -91,6 +93,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
|
||||
public class RestfulServer extends HttpServlet {
|
||||
|
@ -385,8 +388,8 @@ public class RestfulServer extends HttpServlet {
|
|||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement if one has been explicitly defined.
|
||||
* <p>
|
||||
* By default, the ServerConformanceProvider for the declared version of FHIR is used, but this can be changed, or set to <code>null</code>
|
||||
* to use the appropriate one for the given FHIR version.
|
||||
* By default, the ServerConformanceProvider for the declared version of FHIR is used, but this can be changed, or
|
||||
* set to <code>null</code> to use the appropriate one for the given FHIR version.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
|
@ -539,7 +542,7 @@ public class RestfulServer extends HttpServlet {
|
|||
if (nextString.startsWith("_")) {
|
||||
operation = nextString;
|
||||
} else {
|
||||
id = new IdDt(resourceName, nextString);
|
||||
id = new IdDt(resourceName, UrlUtil.unescape(nextString));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,7 +554,7 @@ public class RestfulServer extends HttpServlet {
|
|||
if (id == null) {
|
||||
throw new InvalidRequestException("Don't know how to handle request path: " + requestPath);
|
||||
}
|
||||
id = new IdDt(resourceName + "/" + id.getIdPart() + "/_history/" + versionString);
|
||||
id = new IdDt(resourceName, id.getIdPart(), UrlUtil.unescape(versionString));
|
||||
} else {
|
||||
operation = Constants.PARAM_HISTORY;
|
||||
}
|
||||
|
@ -984,8 +987,8 @@ public class RestfulServer extends HttpServlet {
|
|||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* <p>
|
||||
* By default, the ServerConformanceProvider implementation for the declared version of FHIR is used, but this can be changed, or set to <code>null</code>
|
||||
* if you do not wish to export a conformance statement.
|
||||
* By default, the ServerConformanceProvider implementation for the declared version of FHIR is used, but this can
|
||||
* be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
|
@ -1534,9 +1537,12 @@ public class RestfulServer extends HttpServlet {
|
|||
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path
|
||||
* implementation
|
||||
*
|
||||
* @param requestFullPath the full request path
|
||||
* @param servletContextPath the servelet context path
|
||||
* @param servletPath the servelet path
|
||||
* @param requestFullPath
|
||||
* the full request path
|
||||
* @param servletContextPath
|
||||
* the servelet context path
|
||||
* @param servletPath
|
||||
* the servelet path
|
||||
* @return created resource path
|
||||
*/
|
||||
protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -27,8 +29,8 @@ public class UrlUtil {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UrlUtil.class);
|
||||
|
||||
/**
|
||||
* Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning
|
||||
* and return theEndpoint if the input is invalid.
|
||||
* Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning and return theEndpoint if the input is
|
||||
* invalid.
|
||||
*/
|
||||
public static String constructAbsoluteUrl(String theBase, String theEndpoint) {
|
||||
if (theEndpoint == null) {
|
||||
|
@ -78,7 +80,7 @@ public class UrlUtil {
|
|||
}
|
||||
|
||||
if (theExtensionUrl.length() > parentLastSlashIdx) {
|
||||
return theExtensionUrl.substring(parentLastSlashIdx+1);
|
||||
return theExtensionUrl.substring(parentLastSlashIdx + 1);
|
||||
}
|
||||
|
||||
return theExtensionUrl;
|
||||
|
@ -124,4 +126,18 @@ public class UrlUtil {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static String unescape(String theString) {
|
||||
if (theString == null) {
|
||||
return null;
|
||||
}
|
||||
if (theString.indexOf('%') == -1) {
|
||||
return theString;
|
||||
}
|
||||
try {
|
||||
return URLDecoder.decode(theString, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("UTF-8 not supported, this shouldn't happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.not;
|
|||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -20,6 +21,8 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.net.UrlEscapers;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.BaseResource;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -156,6 +159,25 @@ public class ReadTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithEscapedCharsInId() throws Exception {
|
||||
String id = "ABC!@#$%DEF";
|
||||
String idEscaped = URLEncoder.encode(id, "UTF-8");
|
||||
|
||||
String vid = "GHI:/:/JKL";
|
||||
String vidEscaped = URLEncoder.encode(vid, "UTF-8");
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/" + idEscaped + "/_history/" + vidEscaped);
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class, responseContent).getIdentifierFirstRep();
|
||||
assertEquals(id, dt.getSystem().getValueAsString());
|
||||
assertEquals(vid, dt.getValue().getValueAsString());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
|
@ -166,12 +188,12 @@ public class ReadTest {
|
|||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyProvider patientProvider = new DummyProvider();
|
||||
PatientProvider patientProvider = new PatientProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer();
|
||||
ourCtx = servlet.getFhirContext();
|
||||
servlet.setResourceProviders(patientProvider, new DummyBinaryProvider(), new OrganizationProviderWithAbstractReturnType());
|
||||
servlet.setResourceProviders(patientProvider, new BinaryProvider(), new OrganizationProviderWithAbstractReturnType());
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
|
@ -187,7 +209,7 @@ public class ReadTest {
|
|||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class DummyProvider implements IResourceProvider {
|
||||
public static class PatientProvider implements IResourceProvider {
|
||||
|
||||
@Read(version = true)
|
||||
public Patient read(@IdParam IdDt theId) {
|
||||
|
@ -210,7 +232,7 @@ public class ReadTest {
|
|||
public static class OrganizationProviderWithAbstractReturnType implements IResourceProvider {
|
||||
|
||||
@Read(version = true)
|
||||
public BaseResource findPatient(@IdParam IdDt theId) {
|
||||
public BaseResource findOrganization(@IdParam IdDt theId) {
|
||||
Organization org = new Organization();
|
||||
org.addIdentifier(theId.getIdPart(), theId.getVersionIdPart());
|
||||
org.setId("Organization/1/_history/1");
|
||||
|
@ -227,7 +249,7 @@ public class ReadTest {
|
|||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class DummyBinaryProvider implements IResourceProvider {
|
||||
public static class BinaryProvider implements IResourceProvider {
|
||||
|
||||
@Read(version = true)
|
||||
public Binary findPatient(@IdParam IdDt theId) {
|
||||
|
|
|
@ -27,12 +27,21 @@
|
|||
<b>Important Note: </b>
|
||||
This implementation uses a fairly simple table design, with a
|
||||
single table being used to hold resource bodies (which are stored as
|
||||
GZipped CLOBs) and a set of tables to hold search indexes, tags,
|
||||
CLOBs, optionally GZipped to save space) and a set of tables to hold search indexes, tags,
|
||||
history details, etc. This design is only one of many possible ways
|
||||
of designing a FHIR server so it is worth considering whether it
|
||||
is appropriate for the problem you are trying to solve.
|
||||
</p>
|
||||
|
||||
<subsection name="Getting Started">
|
||||
|
||||
<p>
|
||||
The easiest way to get started with HAPI's JPA server module is
|
||||
to begin with the example project.
|
||||
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue