Return a content-location header on update

This commit is contained in:
jamesagnew 2014-09-29 21:05:55 -04:00
parent 4622af7a82
commit dcccf5553e
3 changed files with 38 additions and 27 deletions

View File

@ -63,6 +63,11 @@
HAPI now logs a single line indicating the StAX implementation being used upon the
first time an XML parser is created.
</action>
<action type="fix">
Update methods on the server did not return a "content-location" header, but
only a "location" header. Both are required according to the FHIR specification.
Thanks to Bill de Beaubien of Systems Made Simple for reporting this!
</action>
</release>
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
<!--

View File

@ -61,8 +61,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
if (!theMethod.getReturnType().equals(MethodOutcome.class)) {
if (!allowVoidReturnType()) {
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName()
+ " method but it does not return " + MethodOutcome.class);
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName() + " method but it does not return " + MethodOutcome.class);
} else if (theMethod.getReturnType() == void.class) {
myReturnVoid = true;
}
@ -89,8 +88,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
}
@Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
switch (theResponseStatusCode) {
case Constants.STATUS_HTTP_200_OK:
case Constants.STATUS_HTTP_201_CREATED:
@ -147,8 +145,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
throw new InternalErrorException("Server method returned invalid resource ID: " + response.getId().getValue());
}
}
OperationOutcome outcome = response != null ? response.getOperationOutcome():null;
OperationOutcome outcome = response != null ? response.getOperationOutcome() : null;
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());
@ -156,19 +154,18 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
return;
}
}
switch (getResourceOperationType()) {
case CREATE:
if (response == null) {
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName()
+ " returned null, which is not allowed for create operation");
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
}
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
} else {
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
}
addLocationHeader(theRequest, servletResponse, response);
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION);
break;
case UPDATE:
@ -178,7 +175,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
}
if (response != null && response.getId() != null) {
addLocationHeader(theRequest, servletResponse, response);
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION);
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_CONTENT_LOCATION);
}
break;
@ -226,7 +224,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
return myReturnVoid;
}
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) {
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation) {
StringBuilder b = new StringBuilder();
b.append(theRequest.getFhirServerBase());
b.append('/');
@ -240,31 +238,38 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
b.append("/" + Constants.PARAM_HISTORY + "/");
b.append(response.getVersionId().getValue());
}
theResponse.addHeader(Constants.HEADER_LOCATION, b.toString());
theResponse.addHeader(headerLocation, b.toString());
}
/*
* @Override public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException { Object[] params = new
* Object[getParameters().size()]; for (int i = 0; i < getParameters().size(); i++) { IParameter param = getParameters().get(i); if (param != null) { params[i] =
* param.translateQueryParametersIntoServerArgument(theRequest, null); } }
* @Override public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse)
* throws BaseServerResponseException, IOException { Object[] params = new Object[getParameters().size()]; for (int
* i = 0; i < getParameters().size(); i++) { IParameter param = getParameters().get(i); if (param != null) {
* params[i] = param.translateQueryParametersIntoServerArgument(theRequest, null); } }
*
* addParametersForServerRequest(theRequest, params);
*
* MethodOutcome response = (MethodOutcome) invokeServerMethod(getProvider(), params);
*
* if (response == null) { if (myReturnVoid == false) { throw new ConfigurationException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() +
* " returned null"); } else { theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); } } else if (!myReturnVoid) { if (response.isCreated()) {
* theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED); StringBuilder b = new StringBuilder(); b.append(theRequest.getFhirServerBase()); b.append('/'); b.append(getResourceName());
* b.append('/'); b.append(response.getId().getValue()); if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) { b.append("/_history/");
* b.append(response.getVersionId().getValue()); } theResponse.addHeader("Location", b.toString()); } else { theResponse.setStatus(Constants.STATUS_HTTP_200_OK); } } else {
* if (response == null) { if (myReturnVoid == false) { throw new ConfigurationException("Method " +
* getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null"); }
* else { theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); } } else if (!myReturnVoid) { if
* (response.isCreated()) { theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED); StringBuilder b = new
* StringBuilder(); b.append(theRequest.getFhirServerBase()); b.append('/'); b.append(getResourceName());
* b.append('/'); b.append(response.getId().getValue()); if (response.getVersionId() != null &&
* response.getVersionId().isEmpty() == false) { b.append("/_history/");
* b.append(response.getVersionId().getValue()); } theResponse.addHeader("Location", b.toString()); } else {
* theResponse.setStatus(Constants.STATUS_HTTP_200_OK); } } else {
* theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); }
*
* theServer.addHeadersToResponse(theResponse);
*
* Writer writer = theResponse.getWriter(); try { if (response != null) { OperationOutcome outcome = new OperationOutcome(); if (response.getOperationOutcome() != null &&
* response.getOperationOutcome().getIssue() != null) { outcome.getIssue().addAll(response.getOperationOutcome().getIssue()); } EncodingUtil encoding =
* BaseMethodBinding.determineResponseEncoding(theRequest .getServletRequest(), theRequest.getParameters()); theResponse.setContentType(encoding.getResourceContentType()); IParser parser =
* encoding.newParser(getContext()); parser.encodeResourceToWriter(outcome, writer); } } finally { writer.close(); } // getMethod().in }
* Writer writer = theResponse.getWriter(); try { if (response != null) { OperationOutcome outcome = new
* OperationOutcome(); if (response.getOperationOutcome() != null && response.getOperationOutcome().getIssue() !=
* null) { outcome.getIssue().addAll(response.getOperationOutcome().getIssue()); } EncodingUtil encoding =
* BaseMethodBinding.determineResponseEncoding(theRequest .getServletRequest(), theRequest.getParameters());
* theResponse.setContentType(encoding.getResourceContentType()); IParser parser = encoding.newParser(getContext());
* parser.encodeResourceToWriter(outcome, writer); } } finally { writer.close(); } // getMethod().in }
*/
protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
@ -279,7 +284,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
protected abstract BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
/**
* For servers, this method will match only incoming requests that match the given operation, or which have no operation in the URL if this method returns null.
* For servers, this method will match only incoming requests that match the given operation, or which have no
* operation in the URL if this method returns null.
*/
protected abstract String getMatchingOperation();
@ -352,5 +358,4 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
}
}
}

View File

@ -69,6 +69,7 @@ public class UpdateTest {
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
}