package com.zandero.rest.writer; import com.zandero.rest.AnnotationProcessor; import com.zandero.rest.annotation.Header; import com.zandero.rest.data.MediaTypeHelper; import com.zandero.rest.data.RouteDefinition; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import java.util.HashMap; import java.util.Map; /** * Response writer interface to implement * use RestRouter.getWriters().register(...) to register a global writer * or use @ResponseWriter annotation to associate REST with given writer */ public interface HttpResponseWriter<T> { Logger log = LoggerFactory.getLogger(HttpResponseWriter.class); void write(T result, HttpServerRequest request, HttpServerResponse response) throws Throwable; /** * @param definition route definition * @param requestMediaType client requested media type .. accept header or null for all * @param response response to add response headers */ default void addResponseHeaders(RouteDefinition definition, MediaType requestMediaType, HttpServerResponse response) { if (!response.ended()) { Map<String, String> headers = new HashMap<>(); // collect all headers to put into response ... // 1. add definition headers headers = join(headers, definition.getHeaders()); // 2. add REST produces headers = join(headers, requestMediaType, definition.getProduces()); // 3. add / override Writer headers Header writerHeader = this.getClass().getAnnotation(Header.class); if (writerHeader != null && writerHeader.value().length > 0) { headers = join(headers, AnnotationProcessor.getNameValuePairs(writerHeader.value())); } // 4. add / override with Writer produces Produces writerProduces = this.getClass().getAnnotation(Produces.class); if (writerProduces != null && writerProduces.value().length > 0) { headers = join(headers, requestMediaType, MediaTypeHelper.getMediaTypes(writerProduces.value())); } // 5. add wildcard if no content-type present if (!headers.containsKey(HttpHeaders.CONTENT_TYPE.toString())) { headers.put(HttpHeaders.CONTENT_TYPE.toString(), MediaType.WILDCARD); } String selectedContentType = headers.get(HttpHeaders.CONTENT_TYPE.toString()); if (requestMediaType == null) { log.trace("No 'Accept' header present in request using first @Produces Content-Type='" + selectedContentType + "', for: " + definition.getPath()); } else { log.trace("Selected Content-Type='" + selectedContentType + "', for: " + definition.getPath()); } // add all headers not present in response for (String name : headers.keySet()) { if (!response.headers().contains(name)) { response.headers().add(name, headers.get(name)); } } } } default Map<String, String> join(Map<String, String> original, Map<String, String> additional) { if (additional != null && additional.size() > 0) { original.putAll(additional); } original.remove(HttpHeaders.CONTENT_TYPE.toString()); // remove content-type headers from output return original; } default Map<String, String> join(Map<String, String> original, MediaType contentMediaType, MediaType[] additional) { if (contentMediaType == null) { contentMediaType = MediaType.WILDCARD_TYPE; } MediaType first = null; // to be used in case no content type matches requested content type if (additional != null && additional.length > 0) { for (MediaType produces : additional) { if (first == null) { first = produces; } if (MediaTypeHelper.matches(contentMediaType, produces)) { original.put(HttpHeaders.CONTENT_TYPE.toString(), MediaTypeHelper.toString(produces)); break; } } } if (original.get(HttpHeaders.CONTENT_TYPE.toString()) == null && first != null) { original.put(HttpHeaders.CONTENT_TYPE.toString(), MediaTypeHelper.toString(first)); } return original; } }