vue3使用iframe嵌入ureport2设计器,解决预览时NullPointerException异常问题

 更新时间:2023年10月11日 09:10:06   作者:slxz001  
这篇文章主要介绍了vue3使用iframe嵌入ureport2设计器,解决预览时NullPointerException异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1. 后端准备

关于SpringBoot集成UReport2的文章网络上有很多,这里只记录2个容易踩坑的地方:

配置类

很多文章在写后端集成的时候,都在配置类中使用@Bean注解创建ServletRegistrationBeanUReportPropertyPlaceholderConfigurer对象。

但在实际使用过程中发现,如果要将SpringBoot创建的DataSource作为UReport2的内置数据源,则需要将@Bean注解放到配置类以外的地方,否则DataSource无法注入。

@Configuration
public class ReportBean {
    //添加 report 的servlet
    @Bean
    public ServletRegistrationBean<Servlet> ureport2Servlet() {
        return new ServletRegistrationBean<>(new UReportServlet(), "/ureport/*");
    }
    //这一步省略了创建配置文件
    @Bean
    public UReportPropertyPlaceholderConfigurer UReportPropertyPlaceholderConfigurer() {
        UReportPropertyPlaceholderConfigurer propertyConfigurer = new UReportPropertyPlaceholderConfigurer();
        propertyConfigurer.setIgnoreUnresolvablePlaceholders(true);
        ClassPathResource pathResource = new ClassPathResource("config/config.properties");
        propertyConfigurer.setLocation(pathResource);
        return propertyConfigurer;
    }
}
@Configuration
//导入ureport-console-context.xml文件
@ImportResource("classpath:config/context.xml")
@Slf4j
public class ReportConfig implements BuildinDatasource {
    @Autowired
    private DataSource dataSource;
    /**
     * 数据源名称
     **/
    @Override
    public String name() {
        return "ReportSource";
    }
    /**
     * 获取连接
     **/
    @Override
    public Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            log.error("Ureport 数据源 获取连接失败!");
            e.printStackTrace();
        }
        return null;
    }
}

report.fileStoreDir配置

在ureport的配置文件中,ureport.fileStoreDir表示报表的xml文件在磁盘上的存储目录。

配置完这个目录,必须手动创建,否则在设计器中保存报表时会报错。

2. 前端嵌入

假设后端集成完毕后,报表设计器的访问路径在http://localhost:9000/ureport/designer

<template>
	<div v-loading="loading" class="h-full">
		<iframe :src="src" frameborder="no" class="wh-full" scrolling="auto" />
	</div>
</template>
<script setup lang="tsx">
import { ref } from 'vue';
const src = ref<string>('/ureport/designer');
const loading = ref<boolean>(true);
</script>

可以看到,我们没有直接将iframe的src指向http://localhost:9000/ureport/designer,否则会由于跨域问题而无法显示。

这里我们配了一个报表设计器的相对路径,并将“ureport”进行前端代理。

'/ureport': {
	target: envConfig.secondUrl,
    changeOrigin: true,
    rewrite: path => path.replace(new RegExp(`^${envConfig.urlPattern}`), '/ureport')
}

这里我们让代理拦截前缀为"/ureport"的请求。

由于报表后端路径也是以"/ureport"开头,所以rewrite中不需要将"/ureport"删掉,也可以不写rewrite。

3. 预览问题

假设我们的前端工程以8000端口启动。

将ureport报表设计器嵌入到iframe后,点击左上角的预览,浏览器将打开一个新的tab页,其URL地址为http://localhost:8000/ureport/preview?_u=p

其中,参数_u表示要预览的报表文件名称,在预览时约定文件名为p。此时页面无法正常打开,只显示NullPointerException。

经过跟踪ureport2源码,发现_u参数没有发送到后端。

4. 问题排查与解决

1.尝试使用后端端口直接访问http://localhost:9000/ureport/preview?_u=p发现可以访问,但无法预览。阅读源码发现ureport预览报表时,报表内容随session传递。因此前后端端口不一致时找不到报表内容,无法预览。

2.尝试新建一个vue页面,来进行请求转发发现行不通。直接转发到后端依然存在跨域问题,通过前端代理进行转发,参数还是传不到后端。

3.使用postman调用前端地址(注意,调用8000端口)http://localhost:8000/ureport/preview?_u=p,发现参数可以传递到后端。

到这一步就比较尴尬了。同一地址,使用postman请求,前端代理可以把URL完整传递到后端。使用浏览器请求,前端代理不发送URL参数到后端。

经过比对和测试,发现浏览器发送请求时,其header头的Accept内容为“text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7”。当使用postman发送请求时,其header头的Accept内容为“*/*”

跟踪了代理的rewrite方法,发现使用浏览器访问时,传入的path是不带参数的。阅读vite的部分源码,并结合查询到的一些资料,发现在配置代理时,除了常规的target、rewrite以外,还可以配置
configure和bypass

源码如下:

export declare interface ProxyOptions extends HttpProxy.ServerOptions {
    /**
     * rewrite path
     */
    rewrite?: (path: string) => string;
    /**
     * configure the proxy server (e.g. listen to events)
     */
    configure?: (proxy: HttpProxy.Server, options: ProxyOptions) => void;
    /**
     * webpack-dev-server style bypass function
     */
    bypass?: (req: http.IncomingMessage, res: http.ServerResponse, options: ProxyOptions) => void | null | undefined | false | string;
}

可以看到,configure和bypass方法,都可以取得一个req变量,其类型是http.IncomeingMessage

跟踪req对象,发现其存在两个属性:

  • req.client.originalUrl,其属性值为/ureport/preview?_u=p
  • req.url,其属性值为/ureport/preview

到这里,终于有了些许思路。我们最终的目的就是利用前端代理来访问ureport的预览界面,只要解决了前端代理的参数传递问题,就可以直接预览了。

因此,目前的思路就是让代理直接转发req.client.originalUrl就好。

因此我们改造一下定义代理的配置语句,增加bypass配置:

'/ureport': {
    target: envConfig.secondUrl,
    changeOrigin: true,
    rewrite: path => path.replace(new RegExp(`^${envConfig.urlPattern}`), '/ureport'),
    bypass: (req, res, proxyOption) => {
      req.url = req.originalUrl;
    }
}

在这里,我们在代理转发请求之前,将他本来要转发的地址强行替换为原url地址。

此时请求的参数可以转发到后端,预览页面成功打开。

5. 总结

1.虽然在分析过程中,知道了浏览器请求和postman请求时,由于Header中的Accept不同导致了不同的结果,但最终还是没有精力去找具体是哪段代码导致了这个差异。换句话说,vite用到的http-proxy组件在不同情况下对URL地址进行了不同的处理,如果有知道原理的大神可以留个言。

2.vue2下没测出来类似的问题,等有时间要查查vue2前端代理用的组件和vue3有什么不同。

3.nginx的代理可以完美转发请求,不存在丢参数的情况。

4.我们最终在bypass中强行修改了代理转发的url地址,并成功打开了页面,但这种做法有违http-proxy的设计原理。目前不清楚这种做法会造成什么其他未知后果,不过想来问题不大,因为bypass只对"/ureport"开头的请求进行了代理,不影响系统的其他请求。

5.这个小问题排查了两三天,看见很多关于ureport2集成的文章下面都有人问这个预览的问题如何解决。所以就临时整理思路写了这篇文章,问题排查部分写的可能比较乱,凑合看吧。以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • validate 注册页的表单数据校验实现详解

    validate 注册页的表单数据校验实现详解

    这篇文章主要为大家介绍了validate 注册页的表单数据校验实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • vue项目中form data形式传参方式

    vue项目中form data形式传参方式

    这篇文章主要介绍了vue项目中form data形式传参方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • vue 文件目录结构详解

    vue 文件目录结构详解

    本篇文章主要介绍了vue 文件目录结构详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • vue中destroyed方法及使用示例讲解

    vue中destroyed方法及使用示例讲解

    这篇文章主要为大家介绍了vue中destroyed方法及使用示例讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Vue transition过渡组件详解

    Vue transition过渡组件详解

    我们现在可以了解一下vue的过渡,vue在插入、更新以及移除DOM元素的时候,提供了很多不同方式过渡的效果,如果在css过渡自动应用class,在过渡钩子函数中使用JavaScript直接操作DOM就可以了
    2022-08-08
  • vue.js实现二级菜单效果

    vue.js实现二级菜单效果

    这篇文章主要为大家详细介绍了vue.js实现二级菜单效果的具体方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • 使用vite发布app存在的所有问题解决方法

    使用vite发布app存在的所有问题解决方法

    最近项目中使用了vue3+vite开发一个App项,下面这篇文章主要给大家介绍了关于使用vite发布app存在的所有问题的解决方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • 说说Vue.js中的functional函数化组件的使用

    说说Vue.js中的functional函数化组件的使用

    这篇文章主要介绍了说说Vue.js中的functional函数化组件的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • 详解vue3中的非父子组件传值

    详解vue3中的非父子组件传值

    这篇文章主要为大家介绍了vue3中的非父子组件传值,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • 关于vue3.0使用axios报错问题

    关于vue3.0使用axios报错问题

    这篇文章主要介绍了vue3.0使用axios报错问题记录,vue-cli3.0安装插件的时候要注意区分vue-cli2.0的命令,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08

最新评论