利用OncePerRequestFilter实现批量路经的转发

案例的实现思路是: 将需要转发的路由抽象成一个默认的模版bean,如果医院有定制的转发逻辑就在默认转发模版bean的基础上扩展实现(也可以不扩展,单独再写一个类),在医院的配置中配置模版key(这个key就是模版bean的名称),这样灵活些,并且在filter中增加了根据医院配置获取bean的方法

1.TransferRegex

public interface TransferRegex {

    /**
     * 必须包含数字及字母
     */
    String numberAndLetterRegex = "^[a-z0-9A-Z]+$";


}

2. AbstractTransfer 黑白名单抽象类

public abstract class AbstractTransfer {

    private String[] urlMatch      = new String[] { "" };

    private String[] whiteUrlMatch = new String[] { "" };

    public void setUrlMatch(String... urlMatch) {
        this.urlMatch = urlMatch;
    }

    public String[] getUrlMatch() {
        return this.urlMatch;
    }

    public String[] getWhiteUrlMatch() {
        return whiteUrlMatch;
    }

    public void setWhiteUrlMatch(String... whiteUrlMatch) {
        this.whiteUrlMatch = whiteUrlMatch;
    }
}

3. TransferHandlerAbstract转发抽象处理器

public abstract class TransferHandlerAbstract extends AbstractTransfer implements InitializingBean {

    public final Log    logger = LogFactory.getLog(getClass());

    public final String prefix = "/redirect";

    /**
     * 中转请求
     * @param req /
     */
    public void transferRequest(String prefix,HttpServletRequest req, HttpServletResponse resp) {
        // 中转
        doTransferRequest(prefix,url(req), req, resp);
    }

    public void transferRequest(String url, HttpServletResponse resp) {
        // 中转
        doTransferRequest(url, resp);
    }

    public void transferRequest(String url) {
        // 中转
        doTransferRequest(url);
    }

    protected String url(HttpServletRequest req) {
        return req.getRequestURI();
    }

    protected void doTransferRequest(String url) {
        // to-do
    }

    protected void doTransferRequest(String url, HttpServletResponse resp) {
        // to-do
    }

    protected void doTransferRequest(String prefix,String url, HttpServletRequest req, HttpServletResponse resp) {
        // to-do
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

}

4.filter实现及注册

1.TransferFilter

@Slf4j
public class TransferFilter extends OncePerRequestFilter implements TransferRegex {

    /**
     * 中转处理器
     */
    private final Map<String, TransferHandlerAbstract> transferHandlerMap;

    /**
     * 路径匹配器
     */
    private final PathMatcher                        pathMatcher = new AntPathMatcher();


    /**
     * redisson
     */
    private final RedissonClient                       redissonClient;

    private final String prefix;


    /**
     * 验证器
     * @param request                   /
     * @param response                  /
     * @param filterChain               /
     * @throws ServletException         /
     * @throws IOException              /
     */
    @SneakyThrows
    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain) throws ServletException,
                                                                      IOException {
        // todo: 配置化(根据医院), 获取redis医院配置,从中获取
        // 因为根据平台走的,所以用户token中获取不到serviceAppid,后期再用户token中获取平台id,双重验证
        // 1. 平台配置了以平台为准 2.平台没配置,获取请求头中的serviceAppid进行二次验证
        String serviceAppid = request.getHeader("serviceAppid");
        if (StringUtils.isNotBlank(serviceAppid)) {
            RMap<String, String> map = redissonClient.getMap(Hospital.HOSPITAL_CONFIGURE_KEY);
            if (null != map && !map.isEmpty()) {
                String s = map.get(serviceAppid);
                if (StringUtils.isBlank(s)) {
                    filterChain.doFilter(request, response);
                }
                ListHospital listHospital = JSON.parseObject(s, ListHospital.class);
                String transferTemplateKey = listHospital.getTransferTemplate();
                if (StringUtils.isNotBlank(transferTemplateKey)
                    && transferHandlerMap.containsKey(transferTemplateKey)) {
                    log.info("transferTemplateKey:{}", transferTemplateKey);
                    String path = request.getRequestURI();
                    TransferHandlerAbstract transferHandler = transferHandlerMap
                        .get(transferTemplateKey);
                    String[] whiteUrlMatch = transferHandler.getWhiteUrlMatch();
                    boolean hasWhiteUrl = getStrategyForPath(whiteUrlMatch,path);
                    if (!hasWhiteUrl) {
                        String[] urlMatch = transferHandler.getUrlMatch();
                        boolean b = getStrategyForPath(urlMatch,path);
                        if (b) {
                            // 执行中转方法
                            transferHandler.transferRequest(prefix,request, response);
                            return;
                        }
                    }
                }
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 找到匹配的url
     * @param paths 所有match url
     * @param path 需要匹配的url
     * @return
     */
    public Boolean getStrategyForPath(String[] paths, String path) {
        List<String> matchingPatterns = new ArrayList<>();
        for (String pattern : paths) {
            if (pathMatcher.match(pattern, path)) {
                matchingPatterns.add(pattern);
            }
        }
        if (!matchingPatterns.isEmpty()) {
            Comparator<String> comparator = pathMatcher.getPatternComparator(path);
            matchingPatterns.sort(comparator);
            String s = matchingPatterns.get(0);
            return StringUtils.isNotBlank(s);
        }
        return false;
    }

    public TransferFilter(Map<String, TransferHandlerAbstract> transferHandlerMap,
                          RedissonClient redissonClient,String prefix) {
        this.transferHandlerMap = transferHandlerMap;
        this.redissonClient = redissonClient;
        this.prefix = prefix;
    }
}

2. TransferAutoConfiguration 注册filter

如果是多模块,可以将除了注册bean之外的其他类放到common,哪儿个项目用到就在哪儿个项目注册filter

@Slf4j
@Configuration
@RequiredArgsConstructor
public class TransferAutoConfiguration {

    private final Map<String, TransferHandlerAbstract> transferHandlerMap;

    private final RedissonClient                       redissonClient;

    @Bean
    public FilterRegistrationBean<TransferFilter> TransferFilterFilterRegistrationBean() {
        log.info("TransferFilter registration ...");
        FilterRegistrationBean<TransferFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new TransferFilter(transferHandlerMap, redissonClient,PLATFORM_API_PATH));
        bean.setOrder(1);
        // 默认全部
        bean.setUrlPatterns(Collections.singletonList("/*"));
        return bean;
    }

}

5.具体的转发模版

AllTransferHandler 默认路经中转模版

@Slf4j
@Component
public class AllTransferHandler extends TransferHandlerAbstract {

    @Override
    public void afterPropertiesSet() {
        logger.info("AllTransferHandler afterPropertiesSet...");
        // 设置匹配match
        setUrlMatch(
                HOSPITAL_API_PATH + "/appointment/createAppointment",
                HOSPITAL_API_PATH + "/appointment/selectByPage",
                HOSPITAL_API_PATH + "/appointment/cancelAppointment/{id}",
                HOSPITAL_API_PATH + "/appointmentRecord/selectByPage",
                HOSPITAL_API_PATH + "/appointmentRecord/selectByPage",
                HOSPITAL_API_PATH + "/appointmentRecord/selectById/**",
                HOSPITAL_API_PATH + "/meal/list",
                HOSPITAL_API_PATH + "/meal/details",
                HOSPITAL_API_PATH + "/meal/itemText/**",
                HOSPITAL_API_PATH + "/mealType/list",
                HOSPITAL_API_PATH + "/item/list",
                HOSPITAL_API_PATH + "/mealItem/list",
                HOSPITAL_API_PATH + "/exclusive/check",
                HOSPITAL_API_PATH + "/itemType/list",
                HOSPITAL_API_PATH + "/mealSchedule/list",
                HOSPITAL_API_PATH + "/scheduleSource/list",
                HOSPITAL_API_PATH + "/team/appoint",
                HOSPITAL_API_PATH + "/team/reschedule",
                HOSPITAL_API_PATH + "/team/{name}/{idCard}/{serviceAppid}/{isOverTeam}/{teamAppointId}",
                HOSPITAL_API_PATH + "/team/getMealDeploy/{idCard}/{teamId}",
                HOSPITAL_API_PATH + "/report/appoint_list",
                HOSPITAL_API_PATH + "/report/detail.connect",
                HOSPITAL_API_PATH + "/appointment/get_code/{id}",
                // 支付
                HOSPITAL_API_PATH + "/pay/doPay",
                HOSPITAL_API_PATH + "/pay/callBack",
                HOSPITAL_API_PATH + "/pay/checkPay",
                HOSPITAL_API_PATH + "/pay/refund",

                // 问卷
                HOSPITAL_API_PATH + "/examAnswer/save",
                HOSPITAL_API_PATH + "/examAnswer/info",
                HOSPITAL_API_PATH + "/examAnswer/exam",

                // 平台
                PLATFORM_API_PATH + "/hospital/list",
                PLATFORM_API_PATH + "/hospital/{hosId}",

                // article
                HOSPITAL_API_PATH + "/article/list",
                HOSPITAL_API_PATH + "/article/id"

        );
        // 白名单
        setWhiteUrlMatch(PLATFORM_API_PATH + "/hospital/position_conversion");
    }


    /**
     * 构造
     */
    public AllTransferHandler() {

    }

    /**
     * 中转请求
     * @param prefix    请求前缀地址
     * @param url       请求地址
     * @param req       /
     * @param resp      /
     */
    @SneakyThrows
    @Override
    public void doTransferRequest(String prefix,String url, HttpServletRequest req, HttpServletResponse resp) {
        String uri = prefix + url.replaceFirst(prefix, this.prefix);
        log.info("doTransferRequest -> url:{}", url);
        // 使用转发不使用重定向(重定向设置参数太过麻烦)
        req.getRequestDispatcher(uri).forward(req, resp);
        //        resp.sendRedirect(uri);
    }
}

6. 案例

因为项目需要适配另一个标准的中间件时间紧迫,并且前台不能变所以就有了此方法,执行流程是:请求(/appointment) -> filter(匹配路由)-> 转发其他路由上(/redirect/appointment)

@RestController
@RequestMapping(HOSPITAL_API_PATH + "/redirect")
@Slf4j
@AllArgsConstructor
public class RedirectRest  {
    @SneakyThrows
    @GetMapping("/appointment/{id}")
    @Operation(summary = "预约", description = "预约")
    @Override
    public ApiRestResult<Object> cancelAppoint(@Parameter(name = "id", description = "预约id") @PathVariable("id") String id,
                                               @Validated CancelAppointVo cancelAppointVo
    ) {
        // urlSuffix 可以医院配置中,缓存的redis
        Object ogj;
        if (cancelAppointVo.getAppointType().equals(IsTeam.CHECK.getCode())) {
            ogj = exec( cancelAppointVo.setId(id), "/appointment", "123456");
        }
        return ApiRestResult.ok(ogj);
    }
}
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值