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 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 HTML_MIME = "text/html";

String format;
String outputFormat;
String baseUrl;

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

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

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

import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.Request;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.Operation;
import org.geoserver.platform.Service;
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;

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

@Override
public Service serviceDispatched(Request request, Service service) throws ServiceException {
Service wfs3 = this.wfs3.getOrCompute(() -> (Service) GeoServerExtensions.bean
("wfsService-3.0"));
Service fallback = this.fallback.getOrCompute(() -> (Service) GeoServerExtensions.bean
("wfsService-2.0"));
Service wfs3 =
this.wfs3.getOrCompute(() -> (Service) GeoServerExtensions.bean("wfsService-3.0"));
Service fallback =
this.fallback.getOrCompute(
() -> (Service) GeoServerExtensions.bean("wfsService-2.0"));
if (wfs3.equals(service) && "GetCapabilities".equals(request.getRequest())) {
request.setServiceDescriptor(fallback);
return fallback;
}
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
*
* @param <T>
*/
public final class Lazy<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,7 @@ private RequestWrapper(HttpServletRequest wrapped) {
} else {
this.outputFormat = f;
}
} else {
this.outputFormat = "getFeature".equals(request) ? RFCGeoJSONFeaturesResponse.MIME : BaseRequest.JSON_MIME;
}
}

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

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

import java.util.Map;

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

@Override
public Object read(Object request, Map kvp, Map rawKvp) throws Exception {
Request dispatcherRequest = Dispatcher.REQUEST.get();
if (kvp.containsKey("outputFormat")) {
Object format = kvp.get("outputFormat");
setFormat(kvp, rawKvp, format);
setOutputFormat(kvp, rawKvp, format);
} else if (kvp.containsKey("f")) {
Object format = kvp.get("f");
setFormat(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);
// }
setOutputFormat(kvp, rawKvp, format);
}

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

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

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

import javax.xml.namespace.QName;

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

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);
Filter filter = getFullFilter(kvp);
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public CollectionsDocument(
URLMangler.URLType.SERVICE);
String linkType = Link.REL_ALTERNATE;
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;
linkTitle = "This document";
}
Expand Down
Original file line number 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) {
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);
}

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

Expand Down Expand Up @@ -46,7 +47,9 @@ public LandingPageDocument(LandingPageRequest request, WFSInfo wfs, Catalog cata
LandingPageDocument.class,
"This document as ",
(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.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 Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
*/
package org.geoserver.wfs3;

import static org.junit.Assert.assertEquals;

import net.sf.json.JSON;
import org.geoserver.test.GeoServerSystemTestSupport;

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 {

Expand All @@ -21,4 +28,18 @@ public void testApiYaml() throws Exception {
String yaml = getAsString("wfs3/api?f=application/x-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.