springboot接收返回xml格式參數、針對不同包設置不同格式全局異常

最近對接不同系統有JSON的有xml的。百度了一番發現springboot可以直接使用jackson的jackson-dataformat-xml包來實現。

直接在pom中增加依賴

<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>
//不同加version 因爲jackson-bom中已經依賴了。 具體原理參看 <optional> 標籤

只需在controller中添加produces 即可定義要返回的類型

@RestController
@RequestMapping("/demo")
public class DemoController {

    /**
     * 統一使用XmlResponse類返回數據。
     *
     * @param id
     * @return 定義 produces  = MediaType.APPLICATION_XML_VALUE 即可將實體返回爲xml字符串
     */
    @PostMapping(value = "/get", produces = MediaType.APPLICATION_XML_VALUE)
    @ResponseBody
    public XmlResponse get(@RegXmlEntity User a, @RequestParam("id") String id) {
        System.out.println("請求之user" + a.getUserName());
        Demo demo = new Demo();
        demo.setId(id);


        XmlResponse xml = new XmlResponse();
        Users users = new Users();
        User u = new User();
        u.setUserName("a");
        List<User> list = new ArrayList<>();
        list.add(u);
        list.add(u);
        users.setU(list);
        return XmlResponse.ok().putOutput(users);

    }


}

返回有了  還需要解析 請求參數。解析請求參數其實只需要加上 consumes 即可。但是我這裏請求參數有標準格式。爲了不影響其他的接口。所以新增一個xml參數解析器


import com.utils.XmlMapperUtils;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;

/**
 * @ClassName: XmlArgumentResolver
只解析 input 節點下的xml數據
 */
public class RegXmlArgumentResolver implements HandlerMethodArgumentResolver {


    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RegXmlEntity.class);//只有包含這個註解的參數才解析
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//先獲取requestbody  注意 inputstream獲取一次之後就清空了。 將requestboddy轉成json 然後再轉成xml字符串最後轉成參數對象
        String requestXml = IOUtils.toString(webRequest.getNativeRequest(HttpServletRequest.class).getInputStream(), Charset.forName("UTF-8"));
        JsonNode json = XmlMapperUtils.xmlMapper.readTree(requestXml);
        Object returnObj = null;
        returnObj = XmlMapperUtils.xmlToBean(XmlMapperUtils.xmlMapper.writeValueAsString(json.get("input")),parameter.getParameterType());
        return returnObj;
    }
    
    
}

//自定義的參數註解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName: RegXmlEntity
 * @Description: 掛號xml實體解析註解
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RegXmlEntity {
}

附上一個 xmlMapper工具類

import com.framework.common.exception.ServiceException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.IOException;

/**
 * @ClassName: XmlMapperUtils
 * @Description: xmlMapper工具類
 */
public class XmlMapperUtils {

    public static final XmlMapper xmlMapper;

    static {
        xmlMapper = new XmlMapper();
        SimpleModule module = new SimpleModule();

        xmlMapper.registerModule(module);
        //xmlMapper.setDefaultUseWrapper(false);
        
        //反序列化時,若實體類沒有對應的屬性,是否拋出JsonMappingException異常,false忽略掉
        xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        
        //字段爲null,自動忽略,不再序列化
        xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        //XML標籤名:使用駱駝命名的屬性名,
        xmlMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);

        //轉化成加下劃線和大寫
        xmlMapper.setPropertyNamingStrategy(new UpperCaseSnackNamingStrategy());

        //設置轉換模式
        xmlMapper.enable(MapperFeature.USE_STD_BEAN_NAMING);
        
    }

    /**
     * @param object
     * @return
     * @Description 將java對象轉化爲xml字符串
     * @Title beanToXml
     */
    public static String beanToXml(Object object) {
        try {
            return xmlMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new ServiceException("Java對象轉換爲Xml失敗", e);
        }
    }

    /**
     * @param xml
     * @param clazz
     * @return
     * @Description 將xml字符串轉化爲java對象
     * @Title xmlToBean
     */
    public static <T> T xmlToBean(String xml, Class<T> clazz) {
        try {
            return xmlMapper.readValue(xml, clazz);
        } catch (IOException e) {
            throw new ServiceException("Xml轉換爲Java對象失敗", e);
        }
    }


    /**
     * @author leizhigang
     * @version 1.0v
     * @ClassName UpperCaseSnackNamingStrategy
     * @date 2020-08-04
     * @description 轉大寫並加下劃線
     */
    public static class UpperCaseSnackNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {
        @Override
        public String translate(String input) {
            if (input == null) {
                return input;
            }
            int length = input.length();
            StringBuilder result = new StringBuilder(length * 2);
            int resultLength = 0;
            boolean wasPrevTranslated = false;
            for (int i = 0; i < length; i++) {
                char c = input.charAt(i);
                if (i > 0 || c != '_') {
                    if (Character.isUpperCase(c)) {
                        if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') {
                            result.append('_');
                            resultLength++;
                        }
                        wasPrevTranslated = true;
                    } else {
                        wasPrevTranslated = false;
                    }
                    c = Character.toUpperCase(c);
                    result.append(c);
                    resultLength++;
                }
            }
            return resultLength > 0 ? result.toString() : input;
        }
    }
}

解析都有了 還需要給不同的包裏設置不同的全局異常

import com.framework.common.exception.ServiceException;
import com.regThirdPartyDocking.utils.XmlResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * 異常處理器
 * 
 */
@RestControllerAdvice(basePackages = "com.xml")//可以指定要鑑定的包
@Order(Ordered.HIGHEST_PRECEDENCE)
public class XrServiceExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 處理自定義異常
	 */
	@ExceptionHandler(ServiceException.class)
	public XmlResponse handleServiceException(ServiceException e){
		XmlResponse xr = new XmlResponse();
		xr.put("ret_code", e.getCode());
		xr.put("ret_msg", e.getMessage());
		return xr;
	}

	@ExceptionHandler(NoHandlerFoundException.class)
	public XmlResponse handlerNoFoundException(Exception e) {
		logger.error(e.getMessage(), e);
		return XmlResponse.error(404, "路徑不存在,請檢查路徑是否正確");
	}

	@ExceptionHandler(DuplicateKeyException.class)
	public XmlResponse handleDuplicateKeyException(DuplicateKeyException e){
		logger.error(e.getMessage(), e);
		return XmlResponse.error("數據庫中已存在該記錄");
	}
	

	@ExceptionHandler(Exception.class)
	public XmlResponse handleException(Exception e){
		logger.error(e.getMessage(), e);
		return XmlResponse.error();
	}
}

 

ps:添加了jackson-dataformat-xml之後發現其他使用json格式的接口默認返回的都是xml格式的了(前端如果未指定application/json的話)。 網上說是xml的格式化優先級比JSON的高。按照網上的設置默認格式化方法還是不行。所以可以參考官網說明:

https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc

import com..common.argumentResolver.RegXmlArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * RegWebMvc配置
 *
 */
@Configuration
public class RegWebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new RegXmlArgumentResolver());
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false).
                favorParameter(true).
                parameterName("mediaType").
                ignoreAcceptHeader(true).
                /*useJaf(false).*/
                defaultContentType(MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML); //默認解析爲json
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章