Skip to content

Commit

Permalink
Adding Accept header negotiation
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed May 28, 2018
1 parent b54ca68 commit 7c7988b
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 38 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ public abstract class BaseRequest {
public static final String XML_MIME = "text/xml"; public static final String XML_MIME = "text/xml";
public static final String HTML_MIME = "text/html"; public static final String HTML_MIME = "text/html";


String format; String outputFormat;
String baseUrl; String baseUrl;


/** /**
* The requested format * The requested format
* @return The format name, or null if not set * @return The format name, or null if not set
*/ */
public String getFormat() { public String getOutputFormat() {
return format; return outputFormat;
} }


/** /**
* Sets the format name * Sets the format name
* @param format * @param outputFormat
*/ */
public void setFormat(String format) { public void setOutputFormat(String outputFormat) {
this.format = format; this.outputFormat = outputFormat;
} }


/** /**
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@


import org.geoserver.ows.AbstractDispatcherCallback; import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.Request; import org.geoserver.ows.Request;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.Operation;
import org.geoserver.platform.Service; import org.geoserver.platform.Service;
import org.geoserver.platform.ServiceException; import org.geoserver.platform.ServiceException;
import org.geoserver.wfs3.response.RFCGeoJSONFeaturesResponse;
import org.springframework.http.HttpHeaders;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;


public class WFS3DispatcherCallback extends AbstractDispatcherCallback { public class WFS3DispatcherCallback extends AbstractDispatcherCallback {
Expand All @@ -19,19 +28,73 @@ public class WFS3DispatcherCallback extends AbstractDispatcherCallback {


@Override @Override
public Service serviceDispatched(Request request, Service service) throws ServiceException { public Service serviceDispatched(Request request, Service service) throws ServiceException {
Service wfs3 = this.wfs3.getOrCompute(() -> (Service) GeoServerExtensions.bean Service wfs3 =
("wfsService-3.0")); this.wfs3.getOrCompute(() -> (Service) GeoServerExtensions.bean("wfsService-3.0"));
Service fallback = this.fallback.getOrCompute(() -> (Service) GeoServerExtensions.bean Service fallback =
("wfsService-2.0")); this.fallback.getOrCompute(
() -> (Service) GeoServerExtensions.bean("wfsService-2.0"));
if (wfs3.equals(service) && "GetCapabilities".equals(request.getRequest())) { if (wfs3.equals(service) && "GetCapabilities".equals(request.getRequest())) {
request.setServiceDescriptor(fallback); request.setServiceDescriptor(fallback);
return fallback; return fallback;
} }
return service; return service;
} }


@Override
public Object operationExecuted(Request request, Operation operation, Object result) {
Service wfs3 =
this.wfs3.getOrCompute(() -> (Service) GeoServerExtensions.bean("wfsService-3.0"));
if (wfs3.equals(request.getServiceDescriptor())) {
String header = request.getHttpRequest().getHeader(HttpHeaders.ACCEPT);
Object parsedRequest = operation.getParameters()[0];
Method formatSetter =
OwsUtils.setter(parsedRequest.getClass(), "outputFormat", String.class);
Method formatGetter =
OwsUtils.getter(parsedRequest.getClass(), "outputFormat", String.class);
try {
// can we manipulate the format, and it's not already set?
if (formatGetter != null
&& formatSetter != null
&& formatGetter.invoke(parsedRequest) == null) {

if (header != null) {
// figure out which format we want to use, take the fist supported one
LinkedHashSet<String> acceptedFormats =
new LinkedHashSet<>(Arrays.asList(header.split("\\s*,\\s*")));
List<String> availableFormats =
DefaultWebFeatureService30.getAvailableFormats(result.getClass());
acceptedFormats.retainAll(availableFormats);
if (!acceptedFormats.isEmpty()) {

String format = acceptedFormats.iterator().next();
setOutputFormat(request, parsedRequest, formatSetter, format);
}
} else {
// handle defaults if really nothing is specified
String defaultType =
"getFeature".equals(request.getRequest())
? RFCGeoJSONFeaturesResponse.MIME
: BaseRequest.JSON_MIME;
setOutputFormat(request, parsedRequest, formatSetter, defaultType);
}
}
} catch (Exception e) {
throw new ServiceException("Failed to handle Accept header", e);
}
}

return super.operationExecuted(request, operation, result);
}

private void setOutputFormat(Request request, Object parsedRequest, Method formatSetter, String outputformat)
throws IllegalAccessException, InvocationTargetException {
request.setOutputFormat(outputformat);
formatSetter.invoke(parsedRequest, outputformat);
}

/** /**
* Thread safe lazy calculation, used to avoid bean circular dependencies * Thread safe lazy calculation, used to avoid bean circular dependencies
*
* @param <T> * @param <T>
*/ */
public final class Lazy<T> { public final class Lazy<T> {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -170,9 +170,7 @@ private RequestWrapper(HttpServletRequest wrapped) {
} else { } else {
this.outputFormat = f; this.outputFormat = f;
} }
} else { }
this.outputFormat = "getFeature".equals(request) ? RFCGeoJSONFeaturesResponse.MIME : BaseRequest.JSON_MIME;
}


// support for the limit parameter // support for the limit parameter
String limit = wrapped.getParameter("limit"); String limit = wrapped.getParameter("limit");
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
*/ */
package org.geoserver.wfs3.kvp; package org.geoserver.wfs3.kvp;


import org.geoserver.ows.Dispatcher;
import org.geoserver.ows.KvpRequestReader; import org.geoserver.ows.KvpRequestReader;
import org.geoserver.ows.Request;
import org.springframework.http.HttpHeaders;


import java.util.Map; import java.util.Map;


Expand All @@ -27,30 +24,18 @@ public BaseKvpRequestReader(Class requestBean) {


@Override @Override
public Object read(Object request, Map kvp, Map rawKvp) throws Exception { public Object read(Object request, Map kvp, Map rawKvp) throws Exception {
Request dispatcherRequest = Dispatcher.REQUEST.get();
if (kvp.containsKey("outputFormat")) { if (kvp.containsKey("outputFormat")) {
Object format = kvp.get("outputFormat"); Object format = kvp.get("outputFormat");
setFormat(kvp, rawKvp, format); setOutputFormat(kvp, rawKvp, format);
} else if (kvp.containsKey("f")) { } else if (kvp.containsKey("f")) {
Object format = kvp.get("f"); Object format = kvp.get("f");
setFormat(kvp, rawKvp, format); setOutputFormat(kvp, rawKvp, format);
} else if (request != null) {
// ignoring for the moment, until the HTML output formats are ready, otherwise
// it won't show up in the browser
// String header = dispatcherRequest.getHttpRequest().getHeader(HttpHeaders.ACCEPT);
// if (header != null) {
// String[] formats = header.split("\\s*,\\s*");
// // TODO: check supported formats and pick the first that's actually supported
// String format = formats[0];
// setFormat(kvp, rawKvp, format);
// }
} }

return super.read(request, kvp, rawKvp); return super.read(request, kvp, rawKvp);
} }


public void setFormat(Map kvp, Map rawKvp, Object format) { public void setOutputFormat(Map kvp, Map rawKvp, Object format) {
kvp.put("format", format); kvp.put("outputFormat", format);
rawKvp.put("format", format); rawKvp.put("outputFormat", format);
} }
} }
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.opengis.filter.spatial.BBOX; import org.opengis.filter.spatial.BBOX;
import org.opengis.geometry.Envelope; import org.opengis.geometry.Envelope;


import javax.xml.namespace.QName;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
Expand All @@ -30,6 +29,8 @@
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;


import javax.xml.namespace.QName;

public class GetFeatureKvpRequestReader extends org.geoserver.wfs.kvp.GetFeatureKvpRequestReader { public class GetFeatureKvpRequestReader extends org.geoserver.wfs.kvp.GetFeatureKvpRequestReader {


public GetFeatureKvpRequestReader(GeoServer geoServer, FilterFactory filterFactory) { public GetFeatureKvpRequestReader(GeoServer geoServer, FilterFactory filterFactory) {
Expand All @@ -41,6 +42,11 @@ public Object read(Object request, Map kvp, Map rawKvp) throws Exception {
GetFeatureType gf = (GetFeatureType) super.read(request, kvp, rawKvp); GetFeatureType gf = (GetFeatureType) super.read(request, kvp, rawKvp);
Filter filter = getFullFilter(kvp); Filter filter = getFullFilter(kvp);
querySet(gf, "filter", Collections.singletonList(filter)); querySet(gf, "filter", Collections.singletonList(filter));
// reset the default, we need to do negotiation, there is generic code to do that
// inside WFS3DispatcherCallback
if (!kvp.containsKey("outputFormat")) {
gf.setOutputFormat(null);
}
return gf; return gf;
} }


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public CollectionsDocument(
URLMangler.URLType.SERVICE); URLMangler.URLType.SERVICE);
String linkType = Link.REL_ALTERNATE; String linkType = Link.REL_ALTERNATE;
String linkTitle = "This document " + " as " + format; String linkTitle = "This document " + " as " + format;
if (format.equals(request.getFormat())) { String outputFormat = request.getOutputFormat();
if (format.equals(outputFormat) || (outputFormat == null && format.equals(BaseRequest.JSON_MIME))) {
linkType = Link.REL_SELF; linkType = Link.REL_SELF;
linkTitle = "This document"; linkTitle = "This document";
} }
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public String getMimeType(Object value, Operation operation) throws ServiceExcep


private String getFormat(Operation operation) { private String getFormat(Operation operation) {
BaseRequest request = (BaseRequest) operation.getParameters()[0]; BaseRequest request = (BaseRequest) operation.getParameters()[0];
Optional<String> format = Optional.ofNullable(request.getFormat()); Optional<String> format = Optional.ofNullable(request.getOutputFormat());
return format.orElse(BaseRequest.JSON_MIME); return format.orElse(BaseRequest.JSON_MIME);
} }


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.geoserver.catalog.Catalog; import org.geoserver.catalog.Catalog;
import org.geoserver.ows.URLMangler; import org.geoserver.ows.URLMangler;
import org.geoserver.wfs.WFSInfo; import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs3.BaseRequest;
import org.geoserver.wfs3.DefaultWebFeatureService30; import org.geoserver.wfs3.DefaultWebFeatureService30;
import org.geoserver.wfs3.LandingPageRequest; import org.geoserver.wfs3.LandingPageRequest;


Expand Down Expand Up @@ -46,7 +47,9 @@ public LandingPageDocument(LandingPageRequest request, WFSInfo wfs, Catalog cata
LandingPageDocument.class, LandingPageDocument.class,
"This document as ", "This document as ",
(format, link) -> { (format, link) -> {
if (format.equals(request.getFormat())) { String outputFormat = request.getOutputFormat();
if (format.equals(outputFormat)
|| (outputFormat == null && BaseRequest.JSON_MIME.equals(format))) {
link.setRel(Link.REL_SELF); link.setRel(Link.REL_SELF);
link.setTitle("This document"); link.setTitle("This document");
} }
Expand Down
23 changes: 22 additions & 1 deletion src/community/wfs3/src/test/java/org/geoserver/wfs3/ApiTest.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
*/ */
package org.geoserver.wfs3; package org.geoserver.wfs3;


import static org.junit.Assert.assertEquals;

import net.sf.json.JSON; import net.sf.json.JSON;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.junit.Test; import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

import java.io.ByteArrayInputStream;


public class ApiTest extends WFS3TestSupport { public class ApiTest extends WFS3TestSupport {


Expand All @@ -21,4 +28,18 @@ public void testApiYaml() throws Exception {
String yaml = getAsString("wfs3/api?f=application/x-yaml"); String yaml = getAsString("wfs3/api?f=application/x-yaml");
System.out.println(yaml); System.out.println(yaml);
} }

@Test
public void testYamlAsAcceptsHeader() throws Exception {
MockHttpServletRequest request = createRequest("wfs3/api");
request.setMethod( "GET" );
request.setContent(new byte[]{});
request.addHeader(HttpHeaders.ACCEPT, "foo/bar, application/x-yaml, text/html");
MockHttpServletResponse response = dispatch(request);
assertEquals(200, response.getStatus());
assertEquals("application/x-yaml", response.getContentType());
String yaml = string(new ByteArrayInputStream(response.getContentAsString().getBytes()));

System.out.println(yaml);
}
} }

0 comments on commit 7c7988b

Please sign in to comment.