☰
用友U8 Webservice二次开发实战:跨语言调用ERP接口指南
2026/10/1 1:47:15 网站建设 项目流程

简介:面向用友U8二次开发人员,提供一套基于Webservice的API调用方案,使外部系统无需安装U8客户端即可远程生成单据、处理审核等业务。资源包共1069个文件,约22.64MB,包含189个dll(涵盖UFIDA.U8系列核心依赖库)、107个cs源码、135个png及72个gif辅助资源,另有asmx、config等Web服务工程文件,结构清晰便于按模块查阅。已有5245人学习下载。内容以完整调用源码为主,可直接参考服务封装与接口暴露方式,理解U8API在Webservice中的调用链;借助所附依赖dll与示例工程,Java、Python等非C#语言平台也能快速对接U8,实现单据创建、审核等操作,有效降低集成门槛。

1. 用友U8的Webservice二次开发:被业务部门追着要接口的那些年

做U8二次开发的工程师应该都遇到过这种场景:业务部门拿着Excel说“帮我们跟OA打通一下”,信息科说“我们这边是Java体系,你的COM组件我们调不动”,而U8的API本身是COM暴露的,跨语言调用需要中间层。这个项目解决的就是这个问题——用Webservice把U8的二次开发API封装成标准SOAP接口,让Java、Python、C#都能调,不再被VB和COM绑死。它适合三类人:要给U8做接口对接的开发、被异构系统集成折磨的ERP顾问、以及想从零搭一套U8 API调用链路的入门者。下面这份实战笔记,我把环境、代码、参数和踩过坑全部拆开讲。

2. 选型与初始化:Webservice凭什么能接U8,环境要准备到什么程度

2.1 U8 API的暴露方式:从COM到Webservice,三种路线的取舍

U8这套ERP从早期版本开始,对外暴露接口的方式大致有三条路线。第一条是VB环境下直接引用COM组件,调用U8的公共对象(比如Voucher、CostObject),这条路的问题是只适合Windows平台,而且VB6的生态在今天已经非常冷门。第二条是用友U8EAI的XML数据交换,通过配置消息格式做单据导入导出,这条路能解决一部分标准场景,但自定义业务逻辑的时候很别扭。第三条就是Webservice,通过标准SOAP协议把U8的登录、单据查询、凭证生成等能力发布成接口,任何语言只要支持HTTP和XML解析就能对接。

选Webservice而不是直接调COM,核心理由是跨语言和跨网络。我遇过不少客户现场是“U8在Windows服务器上,业务系统跑在Linux的Tomcat里”,如果只提供COM组件,Java那边根本没法用。而Webservice走的是HTTP协议,防火墙只要开放80端口就能通,Java端通过Axis2或者原生HttpURLConnection都能调。对于U8二次开发来说,Webservice不是性能最好的方式,但它是兼容性最好的方式——大部分U8对接场景里,业务量是单据级而不是数据仓库级,SOAP的序列化开销完全能接受。

2.2 开发环境准备:U8客户端、VS2022与公共组件引用

在动手之前,环境准备是关键。这里说的是“常用做法”,我一般会在开发机上先装U8客户端,保证能正常登录U8应用服务器。为什么要先装客户端?因为U8的公共组件(比如登录代理那套DLL)在客户端安装时才会注册到系统里,只拿一个引用的DLL复制来复制去,经常会出现运行时找不到类型的问题。

开发工具方面,VS2022创建Webservice有个坑,默认新建项目里找不到“Web服务(ASMX)”模板,需要安装“ASP.NET和Web开发”工作负载,并且项目要选择.NET Framework 4.7.2(不是.NET Core或.NET 6/8)。装好后,在解决方案里新建一个ASP.NET Web应用程序(.NET Framework),然后添加“Web 服务(ASMX)”项。下面是项目里需要引用的核心文件清单,不同U8版本文件名略有差异:

引用文件所在位置用途
UFSoft.U8.Framework.LoginProxy.dllU8安装目录\U8SOFT\Client\Bin登录代理,负责建立U8上下文
UFSoft.U8.Framework.PluginBase.dll同上插件基类,封装常用操作
System.Web.Services.dll系统程序集提供WebMethod机制
System.Xml.dll系统程序集SOAP消息和XML序列化

引用的时候注意,U8的DLL有时候会依赖其他同目录文件,所以引用的DLL不要单独拷走,直接把整个Bin目录加入系统的PATH环境变量,或者把依赖文件一起复制到项目的bin目录下,否则部署到IIS上会报文件找不到。

2.3 最小骨架:一个能发布到IIS的asmx服务

环境准备好后,先写一个最简单的asmx服务验证链路。下面是骨架代码:

using System.Web.Services; using UFSoft.U8.Framework.LoginProxy; namespace U8ApiService { [WebService(Namespace = "http://tempuri.org/U8ApiService")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class U8Api : System.Web.Services.WebService { [WebMethod(Description = "测试连通性")] public string Ping() { return "U8 WebService is alive"; } } }

这段代码的逻辑很直白:用[WebMethod]特性标记对外发布的方法,外部系统通过SOAP协议调用Ping接口,只要能拿到返回值就说明服务发布成功。Namespace参数是SOAP消息的默认命名空间,如果不写,客户端调用时会用http://tempuri.org/,在跨语言调用时容易引起命名空间不匹配。ConformsTo的WsiProfiles.BasicProfile1_1是WebService互操作性的声明,保证Java或Python客户端能正确解析WSDL,这个最好保留。

部署这一步我单独说一下:右键项目发布到IIS,应用程序池要选.NET Framework v4.0,不要选“无托管代码”,否则asmx的HTTP处理程序不会加载。发布完成后,浏览器访问http://localhost/U8ApiService/U8Api.asmx,能看到操作列表就说明部署成功。这个最小值验证过程一定要做,因为后面所有U8相关代码都有外部依赖,如果这一步没跑通,你根本分不清问题是出在U8组件还是Webservice本身。

3. 落地一篇能跑的接口:服务端封装与跨语言调用完整步骤

3.1 服务端实现:把U8登录代理封装成可调用的业务方法

U8 API调用的核心是登录代理。U8的逻辑是“先登录,再操作”,所有业务方法执行前都需要有一个已认证的上下文。我在服务端通常把登录过程和业务调用封装到一个类里,而不是在每个WebMethod里都重写一遍登录逻辑。下面是封装登录代理的代码:

using UFSoft.U8.Framework.LoginProxy; public class U8Context { public static string Login(string server, string db, string user, string password, out string errMsg) { errMsg = string.Empty; UFLoginProxy proxy = new UFLoginProxy(); string token; bool success = proxy.Login(server, db, user, password, out token, out errMsg); if (!success) { return null; } return token; } public static void Logout(string token) { UFLoginProxy proxy = new UFLoginProxy(); proxy.Logout(token); } }

这段代码里有三个关键点。UFLoginProxy是U8登录代理的核心类,它的Login方法参数顺序是服务器名、数据库名、账号、密码,返回的token是后续所有API调用的凭证。Login方法有多个重载版本,这里用的是最常用的五参数版本,不同U8版本(比如16.0和18.0)参数签名可能有差异,以实际安装版本为准。Logout方法必须在业务处理完毕后调用,否则U8的许可会被占用,我遇到过因为忘记退出登录导致并发超过许可上限的情况,现场直接被业务部门投诉。

接下来看业务方法的封装。下面是一个封装U8凭证查询的示例,实际业务里你可以替换成采购订单、销售订单等单据接口:

[WebMethod(Description = "根据单号查询凭证信息")] public string GetVoucher(string token, string voucherType, string voucherNo) { try { // 通过token进入U8上下文 // 调用U8API的Voucher类,常见做法是先获取业务对象再执行查询 // 这一步不同版本的API类名不同,需要按实际引用的程序集调整 // 返回结果是XML字符串,方便跨语言解析 return "<result>success</result>"; } catch (Exception ex) { return "<result>failed</result>" + ex.Message; } }

这里的voucherType是单据类型标识,比如“SA”代表销售订单,“PO”代表采购订单,U8里不同类型走不同的API接口。voucherNo是业务单号。代码中调用的核心逻辑我用注释代替了具体类名,因为U8API的程序集命名在不同版本间调整过多次——有的版本叫U8API.Voucher,有的叫UFSoft.U8.Service.Voucher。我一般会在开发机上用反编译工具确认一下实际版本里的类名,再替换进注释位置,这比照着网上老代码抄要靠谱得多。

服务端部署完成后,一定要用浏览器访问U8Api.asmx页面,点开方法名,它会生成一个HTTP POST的表单测试页。先在这里跑通一次调用,确认U8登录和业务方法都正常,再通知外部系统对接。

3.2 Java端调用:用HttpURLConnection组装SOAP报文

Java调用asmx有两种方式:一种是用CXF或Axis2生成客户端代码,这种方式维护方便,但需要引入一堆依赖,适合大规模对接;另一种是用HttpURLConnection直接拼SOAP报文,不引第三方库,轻量快速。对于内部系统对接,我倾向用第二种,因为U8的接口数量通常不多,没必要为了两三个接口引入一套SOAP框架。

下面是Java端的HTTP调用示例:

import java.io.*; import java.net.*; public class U8SoapClient { public static String callU8Api(String endpoint, String soapAction, String soapBody) throws IOException { URL url = new URL(endpoint); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8"); conn.setRequestProperty("SOAPAction", soapAction); conn.setDoOutput(true); try (OutputStream os = conn.getOutputStream()) { byte[] input = soapBody.getBytes("utf-8"); os.write(input, 0, input.length); } int code = conn.getResponseCode(); try (BufferedReader br = new BufferedReader( new InputStreamReader( code >= 400 ? conn.getErrorStream() : conn.getInputStream(), "utf-8"))) { StringBuilder response = new StringBuilder(); String line; while ((line = br.readLine()) != null) { response.append(line); } return response.toString(); } finally { conn.disconnect(); } } }

这段代码有两个重点。第一是SOAPAction头,asmx服务的每个方法都会生成一个SOAPAction,格式通常是http://tempuri.org/U8ApiService/GetVoucher,这个值可以通过浏览器打开asmx页面的方法说明看到,如果写错会直接返回“请求因HTTP状态404失败”。第二是错误流处理,getErrorStream()用于读取服务端返回的异常信息,如果没有这一行,服务端报错时你只能看到一个200以外的状态码,排查问题全靠猜。

实际调用时,SOAP报文体的格式需要跟WSDL严格一致,我惯用的方式是先把asmx页面的WSDL下载下来,用SOAPUI生成一次请求示例,再把这套结构固化到Java代码里,而不是手抖去拼XML标签。用Java拼SOAP报文还有一个常见坑:XML特殊字符没有转义,比如单据备注里含“<”符号,直接拼字符串会破坏报文结构,必须用StringEscapeUtils.escapeXml10()这类工具处理一下。

3.3 Python端调用:用requests十分钟快速验证接口

Python做U8接口验证非常方便。相比Java要拼SOAPAction头、处理错误流,Python的requests库配合内置的xml解析模块就能搞定,适合做接口连通性验证和快速测试。很多制造业信息科会用Python写小程序把外部系统数据同步到U8,下面是调用示例:

import requests import xml.etree.ElementTree as ET URL = "http://192.168.1.10/U8ApiService/U8Api.asmx" SOAP_ACTION = "http://tempuri.org/U8ApiService/GetVoucher" def call_get_voucher(token, voucher_type, voucher_no): body = f"""<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetVoucher xmlns="http://tempuri.org/U8ApiService"> <token>{token}</token> <voucherType>{voucher_type}</voucherType> <voucherNo>{voucher_no}</voucherNo> </GetVoucher> </soap:Body> </soap:Envelope>""" resp = requests.post(URL, data=body.encode("utf-8"), headers={"Content-Type": "text/xml; charset=utf-8", "SOAPAction": SOAP_ACTION}) resp.raise_for_status() # 解析SOAP返回,常见做法是定位到方法名对应的节点取XML内容 root = ET.fromstring(resp.content) return root.text

这段代码把SOAP报文用f-string拼出来,然后POST到asmx地址。需要注意两点。第一,SOAPAction头在Python里经常被忽略,服务端对SOAPAction不敏感时确实能通,但一旦IIS启用了严格的SOAPAction校验,没有这个头就会返回500,所以必须带上。第二,返回的XML是标准的SOAP封装,实际业务数据嵌在GetVoucherResponse节点下面的GetVoucherResult节点里,用ET解析时要用namespace去匹配,不能直接用标签名找。从效率上说,Python适合联调和轻量同步,如果业务量大,还是建议用C#写Windows服务去调,性能差距很明显。

4. 避坑清单:五个最常见的U8 Webservice翻车现场

4.1 现象:VS2022新建项目找不到“Web服务(ASMX)”模板

VS2022默认的工作负载是面向.NET Core和.NET 6/8的,ASMX这种老技术默认被隐藏了。我第一次搭环境时在“新建项目”里翻了三遍也没找到,还以为是VS版本的问题。

原因:ASMX模板只存在于“.NET Framework”类的项目模板里,而且需要额外安装“ASP.NET和Web开发”工作负载。VS2022的默认安装没有包含这项。

解决:打开Visual Studio Installer,勾选“ASP.NET和Web开发”工作负载,修改安装。新建项目时选择“ASP.NET Web应用程序(.NET Framework)”,Framework选4.7.2,创建后在“添加新项”里就能找到“Web服务(ASMX)”模板了。这条路径跟创建.NET Core的WebApi项目完全是两码事。

4.2 现象:发布到IIS后访问asmx页面返回HTTP 404

服务本地调试能通,发布到IIS后访问U8Api.asmx直接404。这个坑在.NET Framework 4.0以上环境特别容易踩。

原因:应用程序池没有使用.NET Framework的托管运行时。VS发布时默认可能生成的是无托管代码的应用池配置,或者IIS的ISAPI筛选器没有加载。

解决:在IIS管理器里找到发布出来的站点,右键“应用程序池”选择“.NET Framework v4.0.30319”,托管管道模式选“集成”。如果还不行,检查处理程序映射里有没有*.asmx对应System.Web.Services.Protocols.WebServiceHandlerFactory。这个配置不对的话,后面的HTTP 401、404会变着花样来。

4.3 现象:登录U8代理时一直超时,或者报“连接U8服务失败”

通过WebMethod调用U8登录代理,返回超时或“无法连接到U8服务”。这个时候直接调用asmx的Ping方法是正常的,但登录就是不行。

原因:U8应用服务器上的后台服务没有启动。U8登录代理需要依赖U8应用服务器的调度服务,常见的是“UFWDPl平台服务”和“U8服务管理”里的几个后台进程。服务端的防火墙也可能拦截了U8组件之间的DCOM通信。

解决:在U8应用服务器上打开“服务管理”,确认所有U8相关服务都已启动。特别是登录代理依赖的UFWDPl服务,如果没启动,登录请求会一直挂起。开发机与U8服务器之间如果是跨网段,还要在U8服务器的Windows防火墙里放行相关端口,U8的端口一般是一段范围,具体看防火墙日志,客户端登录失败时报的端口号就是线索。

4.4 现象:Java/Python客户端调用报“请求因HTTP状态401失败”

服务端发布好了,用浏览器测试正常,但Java或Python客户端调用时报401未授权。

原因:IIS站点的匿名认证默认是关闭的,或者被配置成了Windows集成认证。浏览器访问时因为是本机或域环境,自动带了Windows凭据,所以能过;但外部系统的HTTP请求不带Windows凭据,就被拒了。

解决:在IIS的“身份验证”功能里,把“匿名身份认证”设为启用,其他认证方式按需关闭。如果你担心接口被任意调用,更好的做法是在WebService内部加一层自己的Token校验,而不是依赖IIS的Windows认证——因为外部系统不是你域里的机器,Windows认证对它们就是一道过不去的坎。

4.5 现象:返回DataTable时客户端解析不出来,SOAP报错

服务端方法返回类型是DataTable,调用端拿到的数据跟表格对不上,甚至直接在SOAP序列化时报错。

原因:ASMX对DataTable的序列化支持有限,DataTable在SOAP里会变成diffgram:diffgram格式,客户端解析很麻烦,而且某些字段类型(比如TimeSpan)在SOAP序列化时会直接失效。

解决:不要把DataTable直接作为返回值,统一转换成List<Dictionary<string, object>>或者直接返回JSON字符串。我一般用List<Dictionary<string, string>>,每一行是一个字典,键是字段名,值是字符串化后的字段值。这样客户端不管用什么语言解析都简单。转换代码多写几行,但调用端能少走很多弯路。

5. 进阶:把接口接进审批流,并用Postman验证整条数据链路

将Webservice接口跟企业审批流对接是U8二次开发的高频需求。典型场景是:OA系统审批通过后,自动在U8里生成采购订单或付款单。这个场景里,Webservice接口扮演的角色是“单据落地器”——审批流只负责业务审批,U8只负责台账数据,两者通过接口握手。下面是一个精简的C#客户端调用链示例:

using System.Net.Http; using System.Xml.Linq; public async Task<string> SyncOrderToU8(string token, string orderXml) { var client = new HttpClient(); var soapBody = $@"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> <soap:Body> <CreateOrder xmlns='http://tempuri.org/U8ApiService'> <token>{token}</token> <orderXml>{orderXml}</orderXml> </CreateOrder> </soap:Body> </soap:Envelope>"; var response = await client.PostAsync("http://u8server/U8ApiService/U8Api.asmx", new StringContent(soapBody, System.Text.Encoding.UTF8, "text/xml")); var stream = await response.Content.ReadAsStreamAsync(); XDocument doc = XDocument.Load(stream); return doc.Root.Value; }

这段代码展示了审批流调用后端服务的基本姿势。orderXml是上游系统传过来的单据数据,服务端解析后调用U8API生成正式单据。这里的关键是token怎么管理——在审批流场景里,上游系统通常是一次性连续调用多个方法,我一般让服务端写一个简单Token池,登录成功后把token缓存在内存里,设置一个两小时的过期时间,避免每次调接口都要重新登录U8。

验证整条数据链路,我习惯用Postman而不是SoapUI。Postman支持手动设置SOAPAction头,也能保存整套请求环境。验证步骤是这样的:第一步,调用Login方法获取token;第二步,调用GetVoucher或CreateOrder方法传入测试单据号;第三步,去U8前端界面查这张单据是否生成、字段是否符合预期;第四步,故意传一个不存在的单号,确认接口返回的错误信息能正常透传到调用端。这个四步走完,才能说链路是通的。

一些性能层面的经验也分享给你。U8登录是有并发许限制的,同一个接口服务里如果每次调用都重新登录、从不退出,并发稍高就会出现登录失败。我一般会在服务端配置一个定时清理任务,定期调Logout释放不活跃的token。另外能用SQL直接查视图解决的查询类接口,就不要去调U8API的业务对象——业务对象内部逻辑复杂,性能开销大,而U8后台有大量现成的业务视图可以直接查数据库,只是要注意用只读账号访问。

从那以后,我每次封装U8接口都强制走一遍“先本机客户端登录验证、再asmx部署、再跨语言调用、再清空缓存”这四步,中间任何一步不通都不往下走。这套流程帮我在后来几个项目里少熬了不少夜,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询