做一个自己搭建的springcloud项目的技术点

下面是一个feign的配置类,它配置了feign的自定义解析器与编码器,和feign调用前的拦截器(主要是调用前带上请求头信息token).
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.macro.mall.common.api.CommonResult;
import com.macro.mall.common.exception.ApiException;
import com.qianda.mall.common.constant.Constants;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author 
 * @version 1.0
 * @since 2020/11/5
 */
@Configuration
@RestControllerAdvice()
public class FeignConfig {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 通用feign响应值解析器
     */
    @Bean
    public Decoder feignCommonDecoder() {
        return (response, type) -> {
            Response.Body body = response.body();
            try (BufferedReader bufferedReader = new BufferedReader(body.asReader(Charset.forName("utf-8")));) {
                String bodyStr = bufferedReader.lines().collect(Collectors.joining());
                if (isDirectParse.test(type)) {
                    return objectMapper.readValue(bodyStr, TypeFactory.defaultInstance().constructType(type));
                }

                Type real;
                if (type instanceof ParameterizedType) {
                    real = ((ParameterizedType) type).getRawType();
                } else {
                    real = type;
                }
                JsonNode jsonNode = objectMapper.readTree(bodyStr);
                int code = jsonNode.get(Constants.RESULT_CODE).intValue();
                if (code != 200) {
                    throw new ApiException(jsonNode.get(Constants.RESULT_MSG).textValue());
                }
                String data = jsonNode.get(Constants.RESULT_DATA) == null ? "" : jsonNode.get(Constants.RESULT_DATA).toString();
                if (StringUtils.isBlank(data)) {
                    if (real == Optional.class) {
                        return Optional.empty();
                    } else {
                        return null;
                    }
                } else {
                    if (real == Optional.class) {
                        return Optional.of(objectMapper.readValue(data, TypeFactory.defaultInstance().constructType(((ParameterizedType) type).getActualTypeArguments()[0])));
                    } else {
                        return objectMapper.readValue(data, TypeFactory.defaultInstance().constructType(type));
                    }
                }
            }
        };
    }

    @Bean
    public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }

    /**
     * 可以带上请求头,一般都把token转到下个服务请求中
     * @return
     */
    @Bean
    RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        if (exclude_headers.contains(name.toLowerCase())) {
                            continue;
                        }
                        String values = request.getHeader(name);
                        requestTemplate.header(name, values);
                    }
                }
            }
        };
    }

    private static final List<String> exclude_headers = Arrays.asList("content-type","content-length");
    private static Predicate<Type> isDirectParse = type -> type == CommonResult.class ||
            (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() == CommonResult.class);


    /**
     * Feign 客户端的日志记录,默认级别为NONE
     * Logger.Level 的具体级别如下:
     * NONE:不记录任何信息
     * BASIC:仅记录请求方法、URL以及响应状态码和执行时间
     * HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
     * FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    /**
     * Feign支持文件上传
     * @param messageConverters
     * @return
     */
    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }


    @ExceptionHandler(value = DecodeException.class)
    public CommonResult handleValidException(DecodeException e) {
        Throwable cause = e.getCause();
        if (cause != null && cause instanceof ApiException) {
            return CommonResult.failed(e.getMessage());
        }
        e.printStackTrace();
        return CommonResult.success("系统异常");
    }


}

下面是一个自定义参数校验(3步):

1:可以定义校验组:分为create,update之类的

import javax.validation.groups.Default;

/**
 * @author 
 * @version 1.0
 * @since 2020/11/2
 */
public interface Create extends Default {
}

2:在参数限定时带上组标识

实体类上如:

@ApiModelProperty(value = "车牌号")
@NotNull(message = INVALID,groups = Create.class)
@Length(max = 20, message = "车牌号长度不得超过20")
private String plateNum;

@ApiModelProperty(value = "载重量")
@NotNull(message = INVALID,groups = Create.class)
private BigDecimal carryWeight;

3:在controller的方法中使用, 这里@Validated(Create.class)表明只验证groups中带有Create.class的和没有groups标识的。

@ApiOperation("新增信息")
@PostMapping
public CommonResult post(@RequestBody @Validated(Create.class) BannerManageVo banner){
    return iBannerService.saveOrUpdate(banner.toBanner())?CommonResult.success(banner):CommonResult.failed();
}

全局异常处理一般为:

/**
 * 全局异常处理
 * Created by macro on 2020/2/27.
 */
@RestControllerAdvice
public class GlobalRestExceptionHandler {

    @ExceptionHandler(value = ApiException.class)
    public CommonResult handle(ApiException e) {
        if (e.getErrorCode() != null) {
            return CommonResult.failed(e.getErrorCode());
        }
        return CommonResult.failed(e.getMessage());
    }

springboot中参数映射,

SpringBoot2.x会自动装载MappingJackson2HttpMessageConverter进行消息转换。而MappingJackson2HttpMessageConverter会获取Spring容器中的ObjectMapper配置,来进行Jackson的序列化和反序列化。ObjectMapper是JSON操作的核心,Jackson的JSON操作都是在ObjectMapper中实现的。

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.EnumDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.databind.util.EnumResolver;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.qianda.mall.common.config.converter.BaseEnumConverterFactory;
import com.qianda.mall.common.enums.BaseEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;

import static com.fasterxml.jackson.databind.DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS;
import static com.macro.mall.common.util.DateFormatters.local_datetime_formatter;

/**
 * @author heqiwen    这里处理枚举的逻辑是:
 * @version 1.0
 * @since 2020/11/4
 */
@Configuration
public class JacksonConfig {

    @Bean
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
    {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        // 全局配置序列化返回 JSON 处理
        SimpleModule simpleModule = new SimpleModule();
        //JSON Long ==> String
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(local_datetime_formatter));
        simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(local_datetime_formatter));
        //序列化枚举,  因为每个枚举都继承了BaseEnum。
        simpleModule.addSerializer(BaseEnum.class, new JsonSerializer<BaseEnum>() {
            @Override
            public void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeObject(value.getValue());
            }
        });
        simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
        /*
        * TODO 类型条件限定过宽且仅做了名称处理 ,这里处理枚举的反序列化。
        * */
        simpleModule.addDeserializer(Enum.class, new CommonDeserializer());
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }


    public static class CommonDeserializer extends JsonDeserializer implements ContextualDeserializer {
        private final Class<BaseEnum> aClass;
        private static BaseEnumConverterFactory baseEnumConverterFactory = BaseEnumConverterFactory.INSTANCE;
        public CommonDeserializer(Class<BaseEnum> aClass) {
            this.aClass = aClass;
        }

        public CommonDeserializer() {
            aClass = null;
        }

        @Override
        public BaseEnum deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
            if(StringUtils.isEmpty(jsonParser.getText())){
                return null;
            }
            return baseEnumConverterFactory.getConverter(aClass).convert(jsonParser.getText());
        }

        public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty property) throws JsonMappingException {
            Class rawCls = ctx.getContextualType().getRawClass();
            if(BaseEnum.class.isAssignableFrom(rawCls)){
                return new CommonDeserializer(rawCls);
            }
            return new EnumDeserializer(EnumResolver.constructFor((Class<Enum<?>>) rawCls, ctx.getConfig().getAnnotationIntrospector()),true);
        }
    }
}

restTemplate发送请求

1.post请求需要用LinkedMultiValueMap来传递参数
String url = "https://xx.xx.com/app/order/getNumber";
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("name", "name");
HttpHeaders headers = new HttpHeaders();
//headers可以详细设置请求头中的信息等
//HttpEntity里面包含了请求方和相应方的请求头和请求体,类似于@RequestBody和@ResponseBody
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(paramMap,headers);
ResponseEntity<String> response = template.exchange(url, HttpMethod.POST, httpEntity, String.class)
    2.Get请求
    get请求的话可以直接在拼接请求路径后直接拼接参数/app/order/getNumber?name=name,也可以使用占位符/app/order/getNumber?name={name},通过Map来传参,但不能使用LinkedMultiValueMap,否则会报错
    String url = "https://xx.xx.com/app/order/getNumber?name={name}";
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.add("name", "name");
    HttpHeaders headers = new HttpHeaders();
    HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(null,headers);
    //使用另一个重载的方法传递参数集合paramMap
    ResponseEntity<String> response = template.exchange(url, HttpMethod.GET, httpEntity, String.class,paramMap);
 

redis序列化设置:

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //必须设置,否则无法将JSON转化为对象,会转化成Map类型
    objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
    serializer.setObjectMapper(objectMapper);//得到json序列化
    
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(serializer);
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(serializer);
    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值