Document interceptor framework
This commit is contained in:
parent
9bb10a5968
commit
794db6a141
|
@ -0,0 +1,80 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public class ServerInterceptors {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) throws DataFormatException, IOException {
|
||||
|
||||
|
||||
// START SNIPPET: resourceExtension
|
||||
// Create an example patient
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier(IdentifierUseEnum.OFFICIAL, "urn:example", "7000135", null);
|
||||
|
||||
// Create an extension
|
||||
ExtensionDt ext = new ExtensionDt();
|
||||
ext.setModifier(false);
|
||||
ext.setUrl("http://example.com/extensions#someext");
|
||||
ext.setValue(new DateTimeDt("2011-01-02T11:13:15"));
|
||||
|
||||
// Add the extension to the resource
|
||||
patient.addUndeclaredExtension(ext);
|
||||
//END SNIPPET: resourceExtension
|
||||
|
||||
|
||||
//START SNIPPET: resourceStringExtension
|
||||
HumanNameDt name = patient.addName();
|
||||
name.addFamily().setValue("Shmoe");
|
||||
StringDt given = name.addGiven();
|
||||
given.setValue("Joe");
|
||||
ExtensionDt ext2 = new ExtensionDt(false, "http://examples.com#moreext", new StringDt("Hello"));
|
||||
given.addUndeclaredExtension(ext2);
|
||||
//END SNIPPET: resourceStringExtension
|
||||
|
||||
String output = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
System.out.println(output);
|
||||
|
||||
|
||||
//START SNIPPET: parseExtension
|
||||
// Get all extensions (modifier or not) for a given URL
|
||||
List<ExtensionDt> resourceExts = patient.getUndeclaredExtensionsByUrl("http://fooextensions.com#exts");
|
||||
|
||||
// Get all non-modifier extensions regardless of URL
|
||||
List<ExtensionDt> nonModExts = patient.getUndeclaredExtensions();
|
||||
|
||||
//Get all non-modifier extensions regardless of URL
|
||||
List<ExtensionDt> modExts = patient.getUndeclaredModifierExtensions();
|
||||
//END SNIPPET: parseExtension
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void foo() {
|
||||
//START SNIPPET: subExtension
|
||||
Patient patient = new Patient();
|
||||
|
||||
ExtensionDt parent = new ExtensionDt(false, "http://example.com#parent");
|
||||
patient.addUndeclaredExtension(parent);
|
||||
|
||||
ExtensionDt child1 = new ExtensionDt(false, "http://example.com#childOne", new StringDt("value1"));
|
||||
parent.addUndeclaredExtension(child1);
|
||||
|
||||
ExtensionDt child2 = new ExtensionDt(false, "http://example.com#childTwo", new StringDt("value1"));
|
||||
parent.addUndeclaredExtension(child2);
|
||||
//END SNIPPET: subExtension
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,13 @@
|
|||
Allow generic client ... OAUTH
|
||||
</action>
|
||||
-->
|
||||
<action type="add">
|
||||
Add server interceptor framework, and new interceptor for logging incoming
|
||||
requests.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add server validation framework for validating resources against the FHIR schemas and schematrons
|
||||
</action>
|
||||
<action type="fix">
|
||||
Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University
|
||||
Health Network for reporting!
|
||||
|
|
|
@ -176,7 +176,8 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||
}
|
||||
invokeServerMethod(params);
|
||||
|
||||
for (IServerInterceptor next : theServer.getInterceptors()) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
|
|
|
@ -149,7 +149,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
}
|
||||
|
||||
OperationOutcome outcome = response != null ? response.getOperationOutcome():null;
|
||||
for (IServerInterceptor next : theServer.getInterceptors()) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, outcome, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
|
|
|
@ -231,7 +231,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
Bundle bundle = RestfulServer.createBundleFromBundleProvider(theServer, response, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, 0, count, null);
|
||||
|
||||
for (IServerInterceptor next : theServer.getInterceptors()) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
|
@ -249,7 +250,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
IResource resource = result.getResources(0, 1).get(0);
|
||||
|
||||
for (IServerInterceptor next : theServer.getInterceptors()) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
|
|
|
@ -156,7 +156,8 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
|
||||
TagList resp = (TagList) invokeServerMethod(params);
|
||||
|
||||
for (IServerInterceptor next : theServer.getInterceptors()) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resp, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
|
|
|
@ -8,7 +8,9 @@ public enum OtherOperationTypeEnum {
|
|||
|
||||
DELETE_TAGS("delete-tags"),
|
||||
|
||||
GET_TAGS("get-tags");
|
||||
GET_TAGS("get-tags"),
|
||||
|
||||
GET_PAGE("get-page");
|
||||
|
||||
private String myCode;
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.OtherOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||
|
@ -420,7 +421,8 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
Bundle bundle = createBundleFromBundleProvider(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction);
|
||||
|
||||
for (IServerInterceptor next : getInterceptors()) {
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
|
@ -433,7 +435,7 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||
for (IServerInterceptor next : myInterceptors) {
|
||||
boolean continueProcessing = next.incomingRequest(theRequest, theResponse);
|
||||
boolean continueProcessing = next.incomingRequestPreProcessed(theRequest, theResponse);
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
}
|
||||
|
@ -583,6 +585,7 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
||||
if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
||||
r.setOtherOperationType(OtherOperationTypeEnum.GET_PAGE);
|
||||
handlePagingRequest(r, theResponse, pagingAction);
|
||||
return;
|
||||
}
|
||||
|
@ -608,7 +611,7 @@ public class RestfulServer extends HttpServlet {
|
|||
requestDetails.setOtherOperationType(resourceMethod.getOtherOperationType());
|
||||
|
||||
for (IServerInterceptor next : myInterceptors) {
|
||||
boolean continueProcessing = next.incomingRequest(requestDetails, theRequest, theResponse);
|
||||
boolean continueProcessing = next.incomingRequestPostProcessed(requestDetails, theRequest, theResponse);
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
}
|
||||
|
@ -781,6 +784,19 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets (or clears) the list of interceptors
|
||||
*
|
||||
* @param theList
|
||||
* The list of interceptors (may be null)
|
||||
*/
|
||||
public void setInterceptors(IServerInterceptor... theList) {
|
||||
myInterceptors.clear();
|
||||
if (theList != null) {
|
||||
myInterceptors.addAll(Arrays.asList(theList));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
|
||||
*/
|
||||
|
|
|
@ -26,12 +26,12 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
|
||||
/**
|
||||
* Provides methods to intercept requests and responses
|
||||
* Provides methods to intercept requests and responses. Note that implementations of this interface may wish
|
||||
* to use {@link InterceptorAdapter} in order to not need to implement every method.
|
||||
*/
|
||||
public interface IServerInterceptor {
|
||||
|
||||
|
@ -46,16 +46,19 @@ public interface IServerInterceptor {
|
|||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>true</code>
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further
|
||||
* interceptors will be called.
|
||||
*/
|
||||
public boolean incomingRequest(HttpServletRequest theRequest, HttpServletResponse theResponse);
|
||||
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
|
||||
|
||||
/**
|
||||
* This method is called just before the actual implementing server method is invoked
|
||||
* This method is called just before the actual implementing server method is invoked.
|
||||
* <p>
|
||||
* Note about exceptions:
|
||||
* </p>
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* A bean containing details about the request that is about to be processed, including
|
||||
|
@ -63,16 +66,16 @@ public interface IServerInterceptor {
|
|||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>true</code>
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further
|
||||
* interceptors will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean incomingRequest(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
|
||||
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream
|
||||
|
@ -86,14 +89,14 @@ public interface IServerInterceptor {
|
|||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>true</code>
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further
|
||||
* interceptors will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
|
@ -110,14 +113,14 @@ public interface IServerInterceptor {
|
|||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>true</code>
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further
|
||||
* interceptors will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
|
@ -133,14 +136,14 @@ public interface IServerInterceptor {
|
|||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>true</code>
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further
|
||||
* interceptors will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
|
@ -156,14 +159,14 @@ public interface IServerInterceptor {
|
|||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>true</code>
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further
|
||||
* interceptors will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
|||
public class InterceptorAdapter implements IServerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean incomingRequest(HttpServletRequest theRequest, HttpServletResponse theResponse) {
|
||||
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingRequest(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,10 +55,10 @@ public class LoggingInterceptor extends InterceptorAdapter {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class);
|
||||
|
||||
private Logger myLogger = ourLog;
|
||||
|
||||
private String myMessageFormat = "${operationType} - ${idOrResourceName}";
|
||||
|
||||
@Override
|
||||
public boolean incomingRequest(final RequestDetails theRequestDetails, final HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
public boolean incomingRequestPostProcessed(final RequestDetails theRequestDetails, final HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
|
||||
// Perform any string substitutions from the message format
|
||||
StrLookup<?> lookup = new MyLookup(theRequest, theRequestDetails);
|
||||
|
|
|
@ -123,3 +123,11 @@ dfn {
|
|||
a,a.externalLink,a:active,a:hover,a:link,a:visited {
|
||||
color: #993300;
|
||||
}
|
||||
|
||||
DIV.sidebar-nav UL LI UL LI {
|
||||
/*
|
||||
list-style: initial;
|
||||
list-style-type: circle;
|
||||
*/
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.6 KiB |
|
@ -64,11 +64,14 @@
|
|||
<item name="Tags" href="./doc_tags.html" />
|
||||
<item name="Validation" href="./doc_validation.html" />
|
||||
</item>
|
||||
<item name="RESTful Client" href="./doc_rest_client.html" />
|
||||
<item name="RESTful Client" href="./doc_rest_client.html" >
|
||||
<item name="Interceptors (client)" href="./doc_rest_client_interceptor.html"/>
|
||||
</item>
|
||||
<item name="RESTful Server" href="./doc_rest_server.html" >
|
||||
<item name="RESTful Operations" href="./doc_rest_operations.html" />
|
||||
<item name="Narrative Generator" href="./doc_narrative.html" />
|
||||
<item name="CORS Support" href="./doc_cors.html" />
|
||||
<item name="Interceptors (server)" href="./doc_rest_server_interceptor.html" />
|
||||
<item name="Web Testing UI" href="./doc_server_tester.html" />
|
||||
</item>
|
||||
<item name="Logging" href="./doc_logging.html" />
|
||||
|
@ -90,7 +93,7 @@
|
|||
<item name="Unit Test Report" href="surefire-report.html" />
|
||||
<item name="FindBugs" href="findbugs.html" />
|
||||
<item name="License" href="license.html" />
|
||||
<item name="Source Code" href="http://sourceforge.net/p/hl7api/fhircode/ci/master/tree/" />
|
||||
<item name="Source Code" href="https://github.com/jamesagnew/hapi-fhir" />
|
||||
</menu>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -352,7 +352,7 @@
|
|||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
|
||||
<subsection name="A Complete Example">
|
||||
|
||||
<p>
|
||||
|
@ -371,8 +371,8 @@
|
|||
|
||||
</section>
|
||||
|
||||
<section name="Configuring the Client">
|
||||
|
||||
<section name="Configuring the HTTP Client">
|
||||
|
||||
<p>
|
||||
RESTful clients (both Generic and Annotation-Driven) use
|
||||
<a href="http://hc.apache.org/httpcomponents-client-ga/">Apache HTTP Client</a>
|
||||
|
@ -392,67 +392,12 @@
|
|||
<a href="./apidocs/ca/uhn/fhir/rest/client/IRestfulClientFactory.html">IRestfulClientFactory</a>
|
||||
class from the FhirContext.
|
||||
</p>
|
||||
|
||||
<subsection name="Interceptors">
|
||||
|
||||
<p>
|
||||
Both generic clients and annotation-driven clients support
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/client/IClientInterceptor.html">Client Interceptors</a>,
|
||||
which may be registered in order to provide specific behaviour to each
|
||||
client request.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following section shows some sample interceptors which may be used.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="HTTP Basic Authorization">
|
||||
|
||||
<p>
|
||||
The following example shows how to configure your client to
|
||||
use a specific username and password in every request.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="security" />
|
||||
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<a name="req_resp_logging"/>
|
||||
<subsection name="Logging Requests and Responses">
|
||||
|
||||
<p>
|
||||
An interceptor is provided with HAPI FHIR which can be used to
|
||||
log every transaction. The interceptor can be configured to be extremely
|
||||
verbose (logging entire transactions including HTTP headers and payload bodies)
|
||||
or simply to log request URLs.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="logging" />
|
||||
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Configuring an HTTP Proxy">
|
||||
|
||||
<p>
|
||||
An HTTP proxy may be configured directly on the
|
||||
restful client factory, as shown below.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="logging" />
|
||||
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
|
||||
<p>
|
||||
Note that individual requests and responses
|
||||
can be tweaked using <a href="./doc_rest_client_interceptor.html">interceptors</a>.
|
||||
</p>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
|
||||
|
||||
<properties>
|
||||
<title>RESTful Client - HAPI FHIR</title>
|
||||
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
|
||||
</properties>
|
||||
|
||||
<body>
|
||||
|
||||
<section name="Client Interceptors">
|
||||
|
||||
<p>
|
||||
Both generic clients and annotation-driven clients support
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/client/IClientInterceptor.html">Client Interceptors</a>,
|
||||
which may be registered in order to provide specific behaviour to each
|
||||
client request.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following section shows some sample interceptors which may be used.
|
||||
</p>
|
||||
|
||||
<subsection name="HTTP Basic Authorization">
|
||||
|
||||
<p>
|
||||
The following example shows how to configure your client to
|
||||
use a specific username and password in every request.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="security" />
|
||||
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<a name="req_resp_logging"/>
|
||||
<subsection name="Logging Requests and Responses">
|
||||
|
||||
<p>
|
||||
An interceptor is provided with HAPI FHIR which can be used to
|
||||
log every transaction. The interceptor can be configured to be extremely
|
||||
verbose (logging entire transactions including HTTP headers and payload bodies)
|
||||
or simply to log request URLs.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="logging" />
|
||||
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Configuring an HTTP Proxy">
|
||||
|
||||
<p>
|
||||
An HTTP proxy may be configured directly on the
|
||||
restful client factory, as shown below.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="logging" />
|
||||
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
|
||||
|
||||
<properties>
|
||||
<title>Server Interceptors - HAPI FHIR</title>
|
||||
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
|
||||
</properties>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- The body of the document contains a number of sections -->
|
||||
<section name="Server Interceptors">
|
||||
|
||||
<macro name="toc">
|
||||
</macro>
|
||||
|
||||
<img src="svg/restful-server-interceptors.svg" alt="Interceptors" align="right"/>
|
||||
|
||||
<p>
|
||||
The RESTful server provides a powerful mechanism for adding cross-cutting behaviour
|
||||
to each incoming request that it processes. This mechanism consists of defining one or
|
||||
more <b>interceptors</b> that will
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Interceptors must implement the
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.html">IServerInterceptor</a>
|
||||
interface (or extend the convenience
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.html">InterceptorAdapter</a>
|
||||
class provided). The RESTful server will normally invoke the interceptor at three
|
||||
points in the execution of the client request.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Before any processing at all is performed on the request,
|
||||
<b>incomingRequestPreProcessed</b> will be invoked. This can be useful
|
||||
if you wish to handle some requests completely outside of HAPI's processing
|
||||
mechanism. If you are handling a request in your interceptor, you may
|
||||
return <code>false</code> from your implementation method to signal to
|
||||
HAPI that processing of the request should stop immediately.
|
||||
</li>
|
||||
<li>
|
||||
Once the request is parsed (but before it is handled),
|
||||
<b>incomingRequestPostProcessed</b> will be invoked. This method has
|
||||
an additional parameter, the
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/method/RequestDetails.html">RequestDetails</a>
|
||||
object which contains details about what operation is about to be
|
||||
called, and what request parameters were receievd with that request.
|
||||
</li>
|
||||
<li>
|
||||
After the operation is handled (by invoking the corresponding ResourceProvider or PlainProvider method),
|
||||
but before the actual response is returned to the client,
|
||||
the <b>outgoingResponse</b> method is invoked.
|
||||
This method also has details about the request in its parameters, but also
|
||||
receives a copy of the response that is about to be returned. Note that
|
||||
there are three implementations of <b>outgoingResponse</b>: The server
|
||||
will invoke the one which corresponds to the response type
|
||||
of the operation being invoked (resource, bundle, etc.)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<br clear="all"/>
|
||||
<subsection name="Registering Interceptors">
|
||||
|
||||
<p>
|
||||
To register an interceptor to the server, simply call
|
||||
either <code>registerInterceptor</code> or <code>setInterceptors</code>
|
||||
on your RestfulServer instance.
|
||||
</p>
|
||||
<p>
|
||||
Note that order is important: The server will invoke
|
||||
<code>incomingRequestPreProcessed</code> and <code>incomingRequestPostProcessed</code>
|
||||
in the same order that they are registered to the server. The server will
|
||||
invoke <code>outgoingResponse</code> in the <b>reverse</b> order to the
|
||||
order in which the interceptors were registered. This means that interceptors
|
||||
can be thought of as "wrapping" the request.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
<section name="Built In Interceptors">
|
||||
|
||||
<p>
|
||||
HAPI also provides built-in interceptors which may be useful.
|
||||
</p>
|
||||
|
||||
<a name="Logging"/>
|
||||
<subsection name="Logging Server Requests">
|
||||
|
||||
<p>
|
||||
The
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.html">LoggingInterceptor</a>
|
||||
can be used to generate a new log line (via SLF4j) for each incoming request. LoggingInterceptor
|
||||
provides a flexible message format that can be used to provide a customized level
|
||||
of detail about each incoming request.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
|
@ -25,6 +25,7 @@ import org.junit.AfterClass;
|
|||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
|
@ -51,22 +52,32 @@ public class InterceptorTest {
|
|||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static RestfulServer servlet;
|
||||
private IServerInterceptor myInterceptor;
|
||||
private IServerInterceptor myInterceptor1;
|
||||
private IServerInterceptor myInterceptor2;
|
||||
|
||||
@Test
|
||||
public void testInterceptorFires() throws Exception {
|
||||
when(myInterceptor.incomingRequest(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor.incomingRequest(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
verify(myInterceptor, times(1)).incomingRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verify(myInterceptor, times(1)).incomingRequest(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verify(myInterceptor, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verifyNoMoreInteractions(myInterceptor);
|
||||
InOrder order = inOrder(myInterceptor1, myInterceptor2);
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verifyNoMoreInteractions(myInterceptor1);
|
||||
verifyNoMoreInteractions(myInterceptor2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,8 +88,9 @@ public class InterceptorTest {
|
|||
|
||||
@Before
|
||||
public void before() {
|
||||
myInterceptor = mock(IServerInterceptor.class);
|
||||
servlet.setInterceptors(Collections.singletonList(myInterceptor));
|
||||
myInterceptor1 = mock(IServerInterceptor.class);
|
||||
myInterceptor2 = mock(IServerInterceptor.class);
|
||||
servlet.setInterceptors(myInterceptor1, myInterceptor2);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
if (this.id) {
|
||||
if (this.id.substring(0,4) == 'inc_') {
|
||||
this.name = '_include';
|
||||
} else {
|
||||
} else if (this.id.indexOf('autogen') == -1) {
|
||||
this.name = this.id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<meta name="description" content="" />
|
||||
<meta name="author" content="" />
|
||||
<script src="js/jquery-2.1.0.min.js"></script>
|
||||
<!-- <script src="js/jquery-2.1.1.js"></script>-->
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -32,7 +32,7 @@ function generateHapiSearch(json, container) {
|
|||
var paramLine = null;
|
||||
if (nextParam.type == 'string') {
|
||||
paramLine = '.where(new StringClientParam("' + nextParam.name + '")';
|
||||
paramLine += nextParam.qualifier = ':exact' ? '.matchesExactly()' : '.matches()';
|
||||
paramLine += nextParam.qualifier == ':exact' ? '.matchesExactly()' : '.matches()';
|
||||
paramLine += '.value("' + nextParam.value + '"))';
|
||||
} else if (nextParam.type == 'id') {
|
||||
paramLine = '.where(new StringClientParam("' + nextParam.name + '")';
|
||||
|
|
|
@ -98,12 +98,12 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
|
|||
var qualifierDropdown = $('<ul />', {'class':'dropdown-menu', role:'menu'});
|
||||
for (var i = 0; i < qualifiers.length; i++) {
|
||||
var nextLink = $('<a>' + qualifiers[i].name+'</a>');
|
||||
var theSearchParamName = qualifiers[i].name;
|
||||
var qualName = qualifiers[i].name;
|
||||
var nextValue = qualifiers[i].value;
|
||||
qualifierDropdown.append($('<li />').append(nextLink));
|
||||
nextLink.click(function(){
|
||||
qualifierInput.val(nextValue);
|
||||
matchesLabel.text(theSearchParamName);
|
||||
matchesLabel.text(qualName);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue