All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.datalking.web.support.AbstractMessageConverterMethodProcessor Maven / Gradle / Ivy

The newest version!
package com.github.datalking.web.support;

import com.github.datalking.common.MethodParameter;
import com.github.datalking.util.CollectionUtils;
import com.github.datalking.util.StringUtils;
import com.github.datalking.util.web.UrlPathHelper;
import com.github.datalking.web.context.request.WebRequest;
import com.github.datalking.web.http.HttpHeaders;
import com.github.datalking.web.http.MediaType;
import com.github.datalking.web.http.ServletServerHttpRequest;
import com.github.datalking.web.http.ServletServerHttpResponse;
import com.github.datalking.web.http.accept.ContentNegotiationManager;
import com.github.datalking.web.http.converter.HttpMessageConverter;
import com.github.datalking.web.servlet.HandlerMapping;
import com.github.datalking.web.servlet.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * 同时支持http请求处理方法的参数解析和返回值处理,同时支持数据类型转换
 *
 * @author yaoo on 4/29/18
 */
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
        implements HandlerMethodReturnValueHandler {

    private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");

    private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper();

    private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper();

    static {
        RAW_URL_PATH_HELPER.setRemoveSemicolonContent(false);
        RAW_URL_PATH_HELPER.setUrlDecode(false);
    }

    // 内置消息转换器支持的格式
    private static final Set WHITELISTED_EXTENSIONS = new HashSet<>(Arrays.asList(
            "txt", "text", "json", "xml", "atom", "rss", "png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));

    private final ContentNegotiationManager contentNegotiationManager;

    private final Set safeExtensions = new HashSet<>();

    protected AbstractMessageConverterMethodProcessor(List> messageConverters) {
        this(messageConverters, null);
    }

    protected AbstractMessageConverterMethodProcessor(List> messageConverters,
                                                      ContentNegotiationManager manager) {

        super(messageConverters);
        this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
        this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
        this.safeExtensions.addAll(WHITELISTED_EXTENSIONS);
    }


    protected ServletServerHttpResponse createOutputMessage(WebRequest webRequest) {
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        return new ServletServerHttpResponse(response);
    }

    protected  void writeWithMessageConverters(T returnValue, MethodParameter returnType, WebRequest webRequest) throws IOException {

        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }

    protected  void writeWithMessageConverters(T returnValue,
                                                  MethodParameter returnType,
                                                  ServletServerHttpRequest inputMessage,
                                                  ServletServerHttpResponse outputMessage) throws IOException {

        Class returnValueClass = returnValue.getClass();
        HttpServletRequest servletRequest = inputMessage.getServletRequest();
        List requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
        List producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);

        Set compatibleMediaTypes = new LinkedHashSet<>();
        for (MediaType requestedType : requestedMediaTypes) {
            for (MediaType producibleType : producibleMediaTypes) {
                if (requestedType.isCompatibleWith(producibleType)) {
                    compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
                }
            }
        }

        if (compatibleMediaTypes.isEmpty()) {
//            throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
        }

        List mediaTypes = new ArrayList<>(compatibleMediaTypes);
//        MediaType.sortBySpecificityAndQuality(mediaTypes);

        MediaType selectedMediaType = null;
        for (MediaType mediaType : mediaTypes) {
            if (mediaType.isConcrete()) {
                selectedMediaType = mediaType;
                break;
            } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
                selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                break;
            }
        }

        if (selectedMediaType != null) {
            selectedMediaType = selectedMediaType.removeQualityValue();
            for (HttpMessageConverter messageConverter : this.messageConverters) {
                if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
                    addContentDispositionHeader(inputMessage, outputMessage);
                    ((HttpMessageConverter) messageConverter).write(returnValue, selectedMediaType, outputMessage);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
                                messageConverter + "]");
                    }
                    return;
                }
            }
        }

//        throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);

    }

    protected List getProducibleMediaTypes(HttpServletRequest request, Class returnValueClass) {
        Set mediaTypes = (Set) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        if (!CollectionUtils.isEmpty(mediaTypes)) {
            return new ArrayList<>(mediaTypes);
        } else if (!this.allSupportedMediaTypes.isEmpty()) {
            List result = new ArrayList<>();
            for (HttpMessageConverter converter : this.messageConverters) {
                if (converter.canWrite(returnValueClass, null)) {
                    result.addAll(converter.getSupportedMediaTypes());
                }
            }
            return result;
        } else {
            return Collections.singletonList(MediaType.ALL);
        }
    }

    private List getAcceptableMediaTypes(HttpServletRequest request) {
        List mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
        return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes);
    }

    private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) {
        MediaType produceTypeToUse = produceType.copyQualityValue(acceptType);
        return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse);
    }

    private void addContentDispositionHeader(ServletServerHttpRequest request,
                                             ServletServerHttpResponse response) {

        HttpHeaders headers = response.getHeaders();
        if (headers.containsKey("Content-Disposition")) {
            return;
        }

        HttpServletRequest servletRequest = request.getServletRequest();
        String requestUri = RAW_URL_PATH_HELPER.getOriginatingRequestUri(servletRequest);

        int index = requestUri.lastIndexOf('/') + 1;
        String filename = requestUri.substring(index);
        String pathParams = "";

        index = filename.indexOf(';');
        if (index != -1) {
            pathParams = filename.substring(index);
            filename = filename.substring(0, index);
        }

        filename = DECODING_URL_PATH_HELPER.decodeRequestString(servletRequest, filename);
        String ext = StringUtils.getFilenameExtension(filename);

        pathParams = DECODING_URL_PATH_HELPER.decodeRequestString(servletRequest, pathParams);
        String extInPathParams = StringUtils.getFilenameExtension(pathParams);

        if (!safeExtension(servletRequest, ext) || !safeExtension(servletRequest, extInPathParams)) {
            headers.add("Content-Disposition", "inline;filename=f.txt");
        }
    }

    private boolean safeExtension(HttpServletRequest request, String extension) {
        if (!StringUtils.hasText(extension)) {
            return true;
        }
        extension = extension.toLowerCase(Locale.ENGLISH);
        if (this.safeExtensions.contains(extension)) {
            return true;
        }
        String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        if (pattern != null && pattern.endsWith("." + extension)) {
            return true;
        }
        if (extension.equals("html")) {
            String name = HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
            Set mediaTypes = (Set) request.getAttribute(name);
            if (!CollectionUtils.isEmpty(mediaTypes) && mediaTypes.contains(MediaType.TEXT_HTML)) {
                return true;
            }
        }
        return false;
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy