首页 > 其他分享 >使用CustomRequestMappingHandlerMapping自定义统一处理前后端接口不一致问题

使用CustomRequestMappingHandlerMapping自定义统一处理前后端接口不一致问题

时间:2022-09-21 06:22:06浏览次数:113  
标签:info String 自定义 pattern org 接口 CustomRequestMappingHandlerMapping new import

spring mvc或spring boot使用RequestMappingHandlerMapping自定义统一处理前后端接口不一致问题

本文地址:https://www.cnblogs.com/muphy/p/16714292.html

场景

以前老项目漏洞修复升级,前端请求有些带.do后缀,有些没带,后端controller接口也是有些带.do后缀,有些没带,而且他们不是一一对应,不知以前是怎么跑得起来,升级之后就很多接口就404了,还无法全局替换修复,因为业务复杂涉及权限等,短时间无法哈清楚,所以只能考虑兼容方案

方式一

修改后端接口,让所有的都支持两种访问方式:@RequestMapping(value = { "/item/index", "/product/index.do" }),工作量超大

方式二

使用面向切面思想,只需要实现RequestMappingHandlerMapping统一处理,让每个接口方法覆盖两种url方式,适用于 spring mvcspring boot ,代码如下:

package me.muphy;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

@Component
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    @Override
    protected boolean isHandler(Class<?> beanType) {
        return super.isHandler(beanType);
    }

    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
        if (info == null) {
            return info;
        }
        try {
            PatternsRequestCondition patternsCondition = info.getPatternsCondition();
            if (patternsCondition == null) {
                return info;
            }
            Set<String> oldPatterns = patternsCondition.getPatterns();
            //Set<String> patterns = new HashSet<>();
            for (String pattern : oldPatterns) {
                if (pattern.toLowerCase(Locale.ROOT).endsWith(".do")) {
                    String p = pattern.replaceAll("\\.[dD][Oo]$", "");
                    oldPatterns.add(p); // set去重
                } else {
                    if (!pattern.matches(".+\\.\\w+$")) {
                        String p = pattern + ".do";
                        oldPatterns.add(p);
                    }
                }
            }
            for (String pattern : oldPatterns) {
                System.out.println("pattern = " + pattern);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e);
        }
        //PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(
        //        patterns.toArray( new String[]{} ), null, null, true, true, null );
        //RequestMappingInfo mappingInfo = new RequestMappingInfo(
        //        null, patternsRequestCondition, info.getMethodsCondition(), info.getParamsCondition(), info.getHeadersCondition(), info.getConsumesCondition(),
        //        info.getProducesCondition(), info.getCustomCondition()
        //);
        return info;
    }
}

方式三

上面的方案对处理当前的问题很好,不过如果复杂一点可能会出现地址冲突,使用 @PostConstruct 等实现延时注册避免冲突

package mu.muphy;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.util.*;

@RestController
@RequestMapping("url")
public class UrlHandleController {

    @Autowired
    private RequestMappingHandlerMapping mapping;

    // 同一个对象追加
    @PostConstruct
    public void register() {
        if (mapping == null) {
            return;
        }
        // 获取所有的url
        Set<String> allUrls = new HashSet<>();
        Map<RequestMappingInfo, HandlerMethod> methods = mapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : methods.entrySet()) {
            RequestMappingInfo info = m.getKey();
            for (String pattern : info.getPatternValues()) {
                allUrls.add(pattern);
            }
        }
        // 注册新的url
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : methods.entrySet()) {
            RequestMappingInfo info = m.getKey();
            PatternsRequestCondition patternsCondition = info.getPatternsCondition();
            Set<String> patterns = patternsCondition.getPatterns();
            Set<String> ps = new HashSet<>();
            for (String pattern : patterns) {
                if (pattern.toLowerCase(Locale.ROOT).endsWith(".do")) {
                    String p = pattern.replaceAll("\\.[dD][Oo]$", "");
                    if (!allUrls.contains(p)) {
                        ps.add(p); // set去重
                    }
                } else {
                    if (!pattern.matches(".+\\.\\w+$")) {
                        String p = pattern + ".do";
                        if (!allUrls.contains(p)) {
                            ps.add(p); // set去重
                        }
                    }
                }
            }
            patterns.addAll(ps);
        }
    }

    // 才分成两个对象
    //@PostConstruct
    public void register2() {
        if (mapping == null) {
            return;
        }
        // 获取所有的url
        Set<String> allUrls = new HashSet<>();
        Map<RequestMappingInfo, HandlerMethod> methods = mapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : methods.entrySet()) {
            RequestMappingInfo info = m.getKey();
            for (String pattern : info.getPatternValues()) {
                allUrls.add(pattern);
            }
        }
        // 注册新的url
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : methods.entrySet()) {
            RequestMappingInfo info = m.getKey();
            HandlerMethod handlerMethod = m.getValue();
            Set<String> patterns = new HashSet<>();
            for (String pattern : info.getPatternValues()) {
                if (pattern.toLowerCase(Locale.ROOT).endsWith(".do")) {
                    String p = pattern.replaceAll("\\.[dD][Oo]$", "");
                    if (!allUrls.contains(p)) {
                        patterns.add(p); // set去重
                    }
                } else {
                    if (!pattern.matches(".+\\.\\w+$")) {
                        String p = pattern + ".do";
                        if (!allUrls.contains(p)) {
                            patterns.add(p); // set去重
                        }
                    }
                }
            }
            if (!patterns.isEmpty()) {
                try {
                    PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(
                        patterns.toArray(new String[]{}), null, null, true, true, null);
                    RequestMappingInfo mappingInfo = new RequestMappingInfo(
                        null, patternsRequestCondition, info.getMethodsCondition(), info.getParamsCondition(), info.getHeadersCondition(), info.getConsumesCondition(),
                        info.getProducesCondition(), info.getCustomCondition()
                    );
                    mapping.registerMapping(mappingInfo, handlerMethod.getBean(), handlerMethod.getMethod());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @GetMapping("/list")
    public List<Map<String, String>> getAllUrl(String parentUrl) {
        if (mapping == null) {
            return new ArrayList<>();
        }
        Map<RequestMappingInfo, HandlerMethod> methods = mapping.getHandlerMethods();
        List<Map<String, String>> list = new ArrayList<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : methods.entrySet()) {
            Map<String, String> map = new HashMap<>();
            RequestMappingInfo info = m.getKey();
            HandlerMethod method = m.getValue();
            PatternsRequestCondition p = info.getPatternsCondition();
            map.put("className", method.getMethod().getDeclaringClass().getName()); // 类名
            map.put("method", method.getMethod().getName()); // 方法名
            map.put("url", Arrays.toString(p.getPatterns().toArray()));
            map.put("parameterTypes", JSON.toJSONString(method.getMethod().getParameterTypes()));
            RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
            for (RequestMethod requestMethod : methodsCondition.getMethods()) {
                map.put("type", requestMethod.toString());
            }
            if (!StringUtils.isEmpty(map.get("url")) && (StringUtils.isEmpty(parentUrl) || map.get("url").contains(parentUrl))) {
                list.add(map);
            }
        }
        return list;
    }

}

测试

var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer 6d611410-60ee-45e6-b64d-85b500a7eb50");

var requestOptions = {
  method: 'GET',
  headers: myHeaders,
  redirect: 'follow'
};

fetch("http://localhost:8881/url/list.do", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));

标签:info,String,自定义,pattern,org,接口,CustomRequestMappingHandlerMapping,new,import
From: https://www.cnblogs.com/muphy/p/16714292.html

相关文章

  • nodejs抓取接口数据(axios)
     demo:varaxios=require("axios");varfs=require("fs");varnodeschedule=require("node-schedule");varhotListUrl="https://weibo.com/ajax/side/hotS......
  • WEB自动化-08-Cypress 接口测试
    8接口测试  在服务和服务、系统和系统之间进行通信时,常常会使用到接口。通过接口测试,可以在项目早期更快发现问题。接口有很多类型,而现阶段使用的接口是基于HTTP协议......
  • 自定义博客园主题
    博客园主题自定义设置主题把自己主题设置流程记录一下,我也是仿照别人教程来做,备份教程一步首先我们可以去github下载压缩包,解压开始设置进入到个人信息>设......
  • java-stream-函数式接口
    一、概述java是面向对象的,对象往往映射现实生活中的某个具体的东西,绝大多数保存在java内存的堆中;java的函数式编程,则可以将对象指向一个函数(行为),而非具体的东西;函数式接......
  • c#基础知识之自定义事件
    自定义事件为了让大家多了解一下关于c#的用法,我开始给大家讲解一下关于c#的一些重要用法,想学的大家也常来参与一下,只要技术够硬,赚钱多多,财富多多,幸福多多,只要想学都都可以......
  • Vue中使用js-audio-recorder插件实现录音功能并实现上传Blob数据到SpringBoot后台接口
    场景浏览器web端实现语音消息录制并在录制结束之后将其上传到后台接口。若依前后端分离版手把手教你本地搭建环境并运行项目:https://blog.csdn.net/BADAO_LIUMANG_QIZHI......
  • C# .net 对外接口返回json字符串序列化
     UserBankCarduserBankCard=newUserBankCard(){BankCard=bankCard,UserID=userID,RealName......
  • 面向对象的7种设计原则(2)-接口隔离原则
    定义InterfaceSegregationPrinciple客户端不应依赖它不需要的接口类间的依赖关系应该建立在最小的接口上其实通俗来理解就是,不要在一个接口里面放很多的方法,这......
  • Qt从QWidget继承实现自定义控件,显示之后不响应鼠标点击,但可以响应Tab按键
    1、自定义类ccustomwidget.h2、自定义类ccustomwidget.cpp3、ccustomwidget.ui4、mainwindow.h5、mainwindow.cpp 6、显示效果(checkBox可以显示鼠标点击,但CCus......
  • go语言中使用接口,以及对接口的理解
    go语言中使用接口,以及对接口的理解接口的简单介绍在任一编程语言中,接口-方法或行为的集合,在功能和该功能的使用者之间构建了一层薄薄的抽象层。在使用接口时,并不需要了......