Document interceptor framework

This commit is contained in:
James Agnew 2014-08-26 18:18:00 -04:00
parent 9bb10a5968
commit 794db6a141
23 changed files with 9563 additions and 109 deletions

View File

@ -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
}
}

View File

@ -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!

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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)
*/

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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>

View File

@ -371,7 +371,7 @@
</section>
<section name="Configuring the Client">
<section name="Configuring the HTTP Client">
<p>
RESTful clients (both Generic and Annotation-Driven) use
@ -393,66 +393,11 @@
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.
Note that individual requests and responses
can be tweaked using <a href="./doc_rest_client_interceptor.html">interceptors</a>.
</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>
</section>
</body>

View File

@ -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>

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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">

View File

@ -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 + '")';

View File

@ -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