Python 官方文档:入门教程 => 点击学习
目录Feign 请求动态URL注意事项Feign重写URL以及RequestMapping场景效果展示整体思路实现Feign 请求动态URL 注意事项 FeignClient 中不要
代码如下:
FeignClient类:
@CompileStatic
@FeignClient(name = "xxxxClient")
public interface XxxFeignClient {
@RequestLine("POST")
ResponseDto notifySomething(URI baseUri, ApproveNotifyDto notifyDto);
@RequestLine("GET")
ResponseDto getSomething(URI baseUri, @QueryMap Map<String, String> queryMap)
}
ClientCaller类:
@CompileStatic
@Slf4j
@Component
@Import(FeignClientsConfiguration.class)
public class CallerService {
private XxxFeignClient xxxFeignClient
@Autowired
public CallerService(Decoder decoder, Encoder encoder) {
xxxFeignClient = Feign.builder()
//.client(client)
.encoder(encoder)
.decoder(decoder)
.target(Target.EmptyTarget.create(XxxFeignClient.class))
}
public ResponseDto notifySomething(String url, XxxxDto dto) {
return xxxFeignClient.notifySomething(URI.create(url), dto)
}
public String getSomething(String url, String userId) {
return xxxFeignClient.getSomething(URI.create(url), ["userId": userId])
}
}
背景
由于项目采用的是 消费层 + api层(FeignClient) +服务层 的方式。
导致原项目有太多代码冗余复制的地方。例如FeignClient上要写@RequestMapping,同时要写请求路径,消费层还要写上RequestBody等等,比较麻烦。遂改进了一下方案,简化日常代码开发的工作量
项目的架构是微服务架构,SpringBoot与SpringCloud均为原生版本。
feign层无需写RequestMapping以及RequestBody或者RequestParam等
public interface UserDemoApi {
ApiResponse<UserDemo> add(UserDemo userDemo);
ApiResponse<UserDemo> findByUserIdAndUsername(String userId,String username);
ApiResponse<PageResult<UserDemo>> findByCondition(UserDemoReqVo reqVo);
}
@FeignClient(value = "${api.feign.method.value}",fallback = UserDemoFeignApiFallbackImpl.class)
public interface UserDemoFeignApi extends UserDemoApi {
}
Feign的服务端层
1. 继承RequestMappingHandlerMapping,重写getMappingFORMethod
重写的内容
public class CustomerRequestMapping extends RequestMappingHandlerMapping {
private final static String METHOD_MAPPING = "/remote/%s/%s";
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method,handlerType);
if(requestMappingInfo!=null){
if(handlerType.isAnnotationPresent(CustomerAnnotation.class)){
if(requestMappingInfo.getPatternsCondition().getPatterns().isEmpty()||
requestMappingInfo.getPatternsCondition().getPatterns().contains("")){
String[] path = getMethodPath(method, handlerType);
requestMappingInfo = RequestMappingInfo.paths(path).build().combine(requestMappingInfo);
}
}
}else{
if(handlerType.isAnnotationPresent(CustomerAnnotation.class)){
String[] path = getMethodPath(method, handlerType);
requestMappingInfo = RequestMappingInfo.paths(path).methods(RequestMethod.POST).build();
}
}
return requestMappingInfo;
}
private String[] getMethodPath(Method method, Class<?> handlerType) {
Class<?>[] interfaces = handlerType.getInterfaces();
String methodClassName = interfaces[0].getSimpleName();
methodClassName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, methodClassName);
String[] path = {String.format(METHOD_MAPPING,methodClassName, method.getName())};
return path;
}
覆盖RequestMappingHandlerMapping
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class VersionControlWEBmvcConfiguration implements WebMvcReGIStrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new CustomerRequestMapping();
}
}
2. 服务层Controller
@RestController
@CustomerAnnotation
@RequestMapping
public class UserDemoController implements UserDemoFeignApi {
private final static Logger logger = LoggerFactory.getLogger(UserDemoController.class);
@Resource
private UserDemoService userDemoService;
@Override
public ApiResponse<UserDemo> add(UserDemo userDemo) {
logger.info("request data:<{}>",userDemo);
return userDemoService.add(userDemo);
}
@Override
public ApiResponse<UserDemo> findByUserIdAndUsername(String userId,String username) {
logger.info("request data:<{}>",userId+":"+username);
return ApiResponse.success(new UserDemo());
}
@Override
public ApiResponse<PageResult<UserDemo>> findByCondition(UserDemoReqVo reqVo) {
logger.info("request data:<{}>",reqVo);
return userDemoService.findByCondition(reqVo);
}
}
自此,Fiegn的服务端的配置已经配置完毕。现在我们来配置Feign的客户端
Feign的客户端配置
重写Feign的上下文SpringMvcContract
核心代码为重写processAnnotationOnClass的内容。
特殊情况处理:在正常情况下,多参数且没有@RequestParams参数注解的情况下,Feign会直接抛异常且终止启动。所以需要对多参数做额外处理
@Component
public class CustomerContact extends springMVCContract {
private final static Logger logger = LoggerFactory.getLogger(CustomerContact.class);
private final static String METHOD_PATTERN_BY = "By";
private final static String METHOD_PATTERN_AND = "And";
private final Map<String, Method> processedMethods = new HashMap<>();
private final static String METHOD_MAPPING = "/remote/%s/%s";
private Map<String, Integer> parameterIndexMap = new ConcurrentHashMap<>(100);
public CustomerContact() {
this(Collections.emptyList());
}
public CustomerContact(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
this(annotatedParameterProcessors,new DefaultConversionService());
}
public CustomerContact(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService) {
super(annotatedParameterProcessors, conversionService);
}
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
RequestTemplate template = data.template();
String configKey = data.configKey();
if(StringUtils.isBlank(template.url())){
template.append(getTemplatePath(configKey));
template.method(RequestMethod.POST.name());
}
// 构造查询条件
templateQuery(template,data);
super.processAnnotationOnClass(data, clz);
}
private void templateQuery(RequestTemplate template,MethodMetadata data){
try{
String configKey = data.configKey();
if(manyParameters(data.configKey())){
Method method = processedMethods.get(configKey);
String methodName = method.getName();
String key = getTemplatePath(configKey);
Integer parameterIndex = 0;
if(parameterIndexMap.containsKey(key)){
parameterIndexMap.put(key,parameterIndex++);
}else{
parameterIndexMap.put(key,parameterIndex);
}
int index = methodName.indexOf(METHOD_PATTERN_BY);
if(index>=0){
String[] parametersName = methodName.substring(index+METHOD_PATTERN_BY.length()).split(METHOD_PATTERN_AND);
String parameterName = parametersName[parameterIndex];
String caseName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, parameterName);
Collection<String> param = addTemplatedParam(template.queries().get(parameterName), caseName);
template.query(caseName,param);
setNameParam(data,caseName,parameterIndex);
}
}
}catch (Exception e){
e.printStackTrace();
logger.error("template construct query failed:<{}>",e.getMessage());
}
}
private String getTemplatePath(String configKey) {
Method method = processedMethods.get(configKey);
int first = configKey.indexOf("#");
String apiName = configKey.substring(0,first);
String methodClassName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, apiName);
return String.format(METHOD_MAPPING,methodClassName,method.getName());
}
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
this.processedMethods.put(Feign.configKey(targetType, method), method);
return super.parseAndValidateMetadata(targetType,method);
}
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
if(manyParameters(data.configKey())){
return true;
}
return super.processAnnotationsOnParameter(data,annotations,paramIndex);
}
private void setNameParam(MethodMetadata data, String name, int i) {
Collection<String>
names =
data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList<String>();
names.add(name);
data.indexToName().put(i, names);
}
private boolean manyParameters(String configKey){
Method method = processedMethods.get(configKey);
return method.getParameterTypes().length > 1;
}
最后还有一处修改
由于我们在方法上没有写上RequestBody注解,所以此处需要进行额外的处理
@Configuration
public class CustomerArgumentResolvers implements HandlerMethodArgumentResolver {
// 基本数据类型
private static final Class[] BASE_TYPE = new Class[]{String.class,int.class,Integer.class,boolean.class,Boolean.class, MultipartFile.class};
@Override
public boolean supportsParameter(MethodParameter parameter) {
//springcloud的接口入参没有写@RequestBody,并且是自定义类型对象 也按jsON解析
if (AnnotatedElementUtils.hasAnnotation(parameter.getContaininGClass(), FeignClient.class)) {
if(parameter.getExecutable().getParameters().length<=1){
return true;
}
}
return false;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
final Type type = parameter.getGenericParameterType();
String parameters = getParameters(nativeWebRequest);
if(applyType(type)){
return parameters;
}else {
return JSON.parseObject(parameters,type);
}
}
private String getParameters(NativeWebRequest nativeWebRequest) throws Exception{
httpservletRequest servletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
String jsonBody = "";
if(servletRequest!=null){
ServletInputStream inputStream = servletRequest.getInputStream();
jsonBody = IOUtils.toString(inputStream);
}
return jsonBody;
}
private boolean applyType(Type type){
for (Class classType : BASE_TYPE) {
if(type.equals(classType)){
return true;
}
}
return false;
}
}
@Configuration
public class CustomerConfigAdapter implements WebMvcConfigurer {
@Resource
private CustomerArgumentResolvers customerArgumentResolvers;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(customerArgumentResolvers);
}
}
以上就是配置的所有内容,整体代码量很少。
但可能需要读下源码才能理解
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。
--结束END--
本文标题: Feign 请求动态URL方式
本文链接: https://www.lsjlt.com/news/153306.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0