1. 项目概述:为什么一个老派企业级平台还在认真做Restful接口?
“普元EOS7.5生成RestFul接口服务”——这个标题乍看像一句内部系统文档里的操作备注,甚至有点过时感。毕竟现在聊微服务、云原生、低代码,动辄就是Spring Cloud Alibaba、Kubernetes Operator、OpenAPI 3.1规范,谁还提“EOS”?但如果你真在某大型国企、金融核心系统、省级政务中台或老牌制造企业的IT部门待过,听到“EOS7.5”,第一反应不是淘汰,而是“哦,那个还在跑着的、改不动又不能停的主干系统”。
我接触过三个不同行业的实际案例:某省社保核心业务系统用EOS7.5承载了近十年的参保登记与待遇发放;某城商行的信贷审批流程引擎底层仍是EOS7.5封装的规则服务;还有一家装备制造集团,其MES与ERP集成层至今靠EOS7.5暴露的SOAP+少量Restful混合接口维系。它们共同的痛点是:前端要上Vue3单页应用、移动端要调用JSON接口、外部监管平台要求按OpenAPI标准对接——而原有EOS7.5默认只提供Web Service(SOAP)和EJB远程调用,原生不支持Restful风格。
所以,“生成RestFul接口服务”不是技术炫技,而是生存刚需:它是在不重构核心业务逻辑、不替换底层工作流引擎、不推翻已有权限模型的前提下,给一头大象装上轻量级API翅膀。关键词“普元EOS7.5”“RestFul接口服务”“生成”三者叠加,指向一个非常具体的动作——不是手写Controller,不是反向代理,而是利用EOS平台自身提供的服务发布机制,将已有的EOS服务组件(比如一个封装了SQL查询的ServiceBean,或一个调用了Java类的ProcessService)自动包装成符合HTTP语义、返回JSON、支持GET/POST/PUT/DELETE的标准REST端点。
这背后涉及三个不可绕开的硬核事实:第一,EOS7.5的Restful能力并非开箱即用,它依赖于特定版本的EOS-Platform-REST模块(必须≥7.5.20210618),且需手动部署到应用服务器;第二,“生成”二字有严格限定——它只能对符合EOS服务契约规范(即实现com.primeton.ext.service.IEOSRestService接口)的Java类生效,普通POJO或Spring Bean无法直接接入;第三,真正的难点不在“生成”,而在“生成之后怎么管”:权限如何继承原有EOS角色体系?异常如何映射为标准HTTP状态码?分页、过滤、排序这些前端强需求,EOS7.5原生根本不支持,必须自己补丁。
适合谁来读这篇?如果你是正在维护EOS7.5系统的开发工程师,被前端团队天天催“给个JSON接口”;如果你是架构师,需要评估在不升级EOS8.x的前提下打通新老系统;或者你是刚接手遗留系统的新人,看到eos-service.xml里一堆 标签却不知道怎么对外暴露——那这篇就是为你写的实操手册,不是理论综述,不讲历史沿革,只说今天下午三点前你能在测试环境跑通的步骤。
2. 整体设计思路:为什么不用网关代理,而坚持在EOS层生成?
很多人第一反应是:“既然EOS7.5不原生支持Restful,干嘛不加一层Nginx或Spring Cloud Gateway做反向代理,把SOAP请求转成JSON?”我试过,也帮客户踩过坑,结论很明确:代理方案在EOS7.5场景下是饮鸩止渴。原因有三,且每一条都直击生产环境命门。
第一,SOAP到JSON的语义失真无法避免。EOS7.5的SOAP接口大量使用复杂类型嵌套、自定义XML Schema、WS-Security头信息。比如一个典型的参保信息查询SOAP响应,根节点是 ns:getPersonInfoResponse ,里面包裹 ,再里面才是 等字段。用XSLT或JSON转换器强行映射,遇到空值、数组长度为0、枚举值缺失等情况,极易产生字段丢失或类型错乱。我们曾在一个社保项目中发现,代理层将 错误转为空字符串"",导致前端判断参保状态时永远不为null,结果把“未参保”显示成“参保中”。而原生Restful生成,是从Java方法签名直接反射出JSON结构,字段名、类型、可空性全部由Java注解(@NotNull、@Size)和返回类型决定,零中间转换。
第二,权限体系彻底割裂。EOS7.5的权限控制粒度极细:可以精确到“某个用户对某个流程实例的某个操作按钮是否有权点击”。这套权限数据存在EOS自己的数据库表(如t_biz_role_privilege),并由EOS SecurityManager统一校验。如果走网关代理,权限校验必须在网关层重写一遍,等于把EOS十年积累的权限模型全部废弃。更麻烦的是,EOS的会话管理(SessionID绑定到EOS-UserContext)和网关的JWT Token完全不兼容,强行集成会导致单点登录失效、审计日志无法关联真实操作人。而原生Restful服务启动时,会自动注入EOS的SecurityFilter,所有请求先过EOS的AccessControlService,权限判断逻辑一行代码都不用改。
第三,事务边界和异常处理失控。EOS7.5的服务调用天然支持跨数据库事务(通过JTA/XA),比如一个服务同时更新Oracle的人员表和MySQL的档案表。如果用代理,事务上下文在SOAP调用时才开启,而Restful请求到达网关时事务根本没启动,最终变成两个独立事务,数据一致性全靠运气。异常方面,EOS7.5定义了上百种业务异常(如BizException、ProcessException),每种对应不同的错误码和提示语,前端需要根据errorCode跳转不同错误页面。代理层若只返回HTTP 500,等于把所有业务错误降级为“服务器炸了”,运维根本无法定位是流程超时还是参数校验失败。
所以我们的整体设计思路非常朴素:不绕路,不嫁接,就在EOS7.5的容器内,用它认可的方式,生成它能理解的Restful服务。具体分三步走:
- 环境就绪:确认EOS-Platform-REST模块已正确部署,且版本匹配;
- 服务改造:将目标业务服务(无论是EJB ServiceBean还是普通Java类)改造为实现IEOSRestService接口,并添加必要的REST注解;
- 发布管控:通过EOS管理控制台或配置文件,声明该服务的URL路径、HTTP方法、参数绑定规则,并启用EOS内置的JSON序列化器。
这个思路看似保守,实则最稳。它把所有不确定性锁死在EOS框架内部,所有日志、监控、链路追踪都能复用EOS已有的APM探针,上线后运维同学不用学新工具,看熟悉的EOS日志就能排查问题。
3. 核心细节解析:从Java类到Restful端点的七道工序
“生成Restful接口”听起来像一键操作,但在EOS7.5里,它是一套严谨的契约式开发流程。我把它拆解为七个不可跳过的工序,每个工序都有明确的技术意图和常见陷阱。下面以一个真实案例说明:为某银行信贷系统中“查询客户授信额度”功能生成Restful接口,原始服务是一个名为CreditLimitService的Java类,方法签名为public CreditLimitResult getCreditLimit(String customerId)。
3.1 工序一:确认EOS-Platform-REST模块已激活
这不是简单的“检查jar包是否存在”。EOS7.5的Restful能力由独立模块eos-platform-rest提供,它不像Spring Boot Starter那样自动装配。必须手动完成三件事:
- 将eos-platform-rest-7.5.20210618.jar(注意:必须是2021年6月18日及之后的版本,早期7.5.0版本此模块功能残缺)放入$EOS_HOME/platform/lib目录;
- 修改$EOS_HOME/platform/conf/eos-platform.properties,添加配置项rest.enable=true;
- 重启EOS应用服务器(WebLogic或Tomcat),观察启动日志中是否出现“[EOS-REST] Restful service module initialized successfully”字样。
提示:很多团队卡在这一步,因为误以为只要jar包在lib目录就行。实际上,eos-platform.properties中的开关必须显式开启,否则Restful Filter根本不会注册到Servlet容器中。我们曾遇到一个案例,开发环境能跑通,生产环境死活404,最后发现是生产环境的properties文件被运维同事回滚到了旧版本,rest.enable被注释掉了。
3.2 工序二:服务类实现IEOSRestService接口
这是EOS7.5的硬性契约。不能只加@RestController注解(那是Spring MVC的),必须让服务类实现com.primeton.ext.service.IEOSRestService接口。该接口只有一个方法:
public interface IEOSRestService { Object execute(HttpServletRequest request, HttpServletResponse response) throws Exception; }但别慌,你不需要手动实现execute()方法。EOS提供了抽象基类AbstractEOSRestService,它已经完成了HTTP请求解析、响应写入、异常捕获等模板逻辑。你的任务是继承它,并重写doExecute()方法:
public class CreditLimitRestService extends AbstractEOSRestService { @Override protected Object doExecute(HttpServletRequest request, HttpServletResponse response) throws Exception { // 1. 从request中提取customerId参数 String customerId = request.getParameter("customerId"); // 2. 调用原始业务服务 CreditLimitService service = new CreditLimitService(); CreditLimitResult result = service.getCreditLimit(customerId); // 3. 返回结果,EOS会自动序列化为JSON return result; } }注意:这里的关键是“返回result对象”,而不是response.getWriter().write(jsonStr)。EOS的Restful机制依赖于返回值的类型反射,只有返回POJO对象,它才会调用内置的Jackson序列化器。如果手动写响应流,EOS的JSON格式化、HTTP状态码设置、跨域头添加等功能全部失效。
3.3 工序三:添加REST风格注解(非Spring,是EOS自定义)
EOS7.5不识别javax.ws.rs.*注解(那是JAX-RS标准),它有一套自己的注解体系,定义在com.primeton.ext.annotation.rest包下。必须为服务类添加@RestService注解,并为方法指定HTTP方法和路径:
@RestService( name = "creditLimit", path = "/api/v1/credit/limit", method = HttpMethod.GET ) public class CreditLimitRestService extends AbstractEOSRestService { // ... doExecute实现 }其中,name属性是服务在EOS内部的唯一标识,用于后续权限配置;path属性是对外暴露的完整URL路径(注意:前面不需要加context-path,EOS会自动拼接);method指定HTTP动词。如果需要POST接收JSON Body,需改为method = HttpMethod.POST,并在doExecute中用request.getInputStream()读取原始字节流(EOS不自动解析POST Body为对象,这点比Spring弱)。
3.4 工序四:配置服务发布元数据(eos-service.xml)
EOS7.5的服务注册中心是基于XML的。必须在$EOS_HOME/deploy/services目录下,为该服务创建对应的XML配置文件(如credit-limit-service.xml),内容如下:
<service xmlns="http://www.primeton.com/schema/service" name="creditLimit" class="com.bank.credit.rest.CreditLimitRestService" scope="singleton"> <property name="timeout" value="30000"/> <property name="maxRetry" value="2"/> </service>重点在于name属性必须与@RestService注解中的name完全一致(大小写敏感),class属性指向你的Rest服务类全限定名。scope设为singleton是因为Rest服务是无状态的,没必要每次请求都新建实例。
实操心得:很多开发者把class写成原始业务类(CreditLimitService)而非Rest包装类(CreditLimitRestService),导致EOS容器加载的是SOAP服务,Restful路径根本注册不上。一个快速验证方法是启动后访问http://localhost:7001/eos/rest/services,能看到所有已发布的Rest服务列表,如果列表里没有你的服务名,一定是XML配置错了。
3.5 工序五:启用EOS内置JSON序列化器
EOS7.5默认使用自己的XML序列化器,要输出JSON,必须显式启用。在$EOS_HOME/platform/conf/rest-config.xml中,找到 节点,取消注释并确保enabled="true":
<json-serializer enabled="true" date-format="yyyy-MM-dd HH:mm:ss" include-nulls="false"/>date-format决定了java.util.Date类型如何转为JSON字符串;include-nulls控制是否序列化值为null的字段(建议设为false,避免前端收到大量null字段)。这个配置影响所有Rest服务,无法按服务单独设置。
3.6 工序六:配置跨域与HTTP头(非CORS,是EOS特有方式)
EOS7.5不支持标准的CORS配置(如@CrossOrigin),它通过全局HTTP响应头配置实现。在$EOS_HOME/platform/conf/rest-config.xml中,添加 节点:
<response-header> <header name="Access-Control-Allow-Origin" value="*"/> <header name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS"/> <header name="Access-Control-Allow-Headers" value="Content-Type,X-Requested-With"/> </response-header>注意:value="*"在生产环境不安全,应替换为具体前端域名(如https://bank-frontend.example.com)。另外,OPTIONS预检请求由EOS自动处理,无需额外配置。
3.7 工序七:权限绑定(关键!否则403)
这是最容易被忽略的工序。即使服务能访问,也会因权限不足返回403 Forbidden。必须在EOS管理控制台(http://localhost:7001/eos/console)中完成:
- 进入【系统管理】→【服务管理】→【Restful服务】;
- 找到name为"creditLimit"的服务,点击【权限配置】;
- 选择需要授权的角色(如“信贷查询员”),勾选“可访问”;
- 点击【保存】。
权限数据最终写入数据库表t_rest_service_privilege。如果不走控制台,也可直接执行SQL:
INSERT INTO t_rest_service_privilege (service_name, role_id, privilege_type) VALUES ('creditLimit', (SELECT role_id FROM t_biz_role WHERE role_code='CREDIT_QUERY'), 'READ');常见问题:开发环境能访问,生产环境403。排查顺序一定是:先确认控制台里该服务是否已授权给当前登录用户的角色;再查t_rest_service_privilege表是否有对应记录;最后确认用户登录时是否真的获得了该角色(查t_user_role_rel表)。我们曾在一个项目中发现,运维部署时漏执行了权限初始化SQL脚本,导致所有Rest服务默认无权限。
4. 实操过程详解:从零开始部署一个可用的Restful服务
现在,我们把前面七道工序串起来,用一个可立即执行的完整流程,带你从空目录部署出第一个可用的Restful服务。假设你的EOS7.5环境已安装在/opt/eos75,应用服务器为WebLogic 12c。
4.1 准备工作:验证基础环境
首先确认EOS-Platform-REST模块已就位:
# 检查jar包是否存在且版本正确 ls -l /opt/eos75/platform/lib/eos-platform-rest-*.jar # 应输出类似:eos-platform-rest-7.5.20210618.jar # 检查配置开关 grep "rest.enable" /opt/eos75/platform/conf/eos-platform.properties # 应输出:rest.enable=true如果任一检查失败,立即修正并重启WebLogic:
# 重启WebLogic(以Linux为例) cd /opt/weblogic/user_projects/domains/base_domain/bin ./stopWebLogic.sh ./startWebLogic.sh启动后,查看日志:
tail -f /opt/weblogic/user_projects/domains/base_domain/servers/AdminServer/logs/AdminServer.log | grep "EOS-REST" # 等待出现"Restful service module initialized successfully"4.2 编写服务类:CreditLimitRestService.java
创建Java源文件,路径为/src/main/java/com/bank/credit/rest/CreditLimitRestService.java:
package com.bank.credit.rest; import com.primeton.ext.service.AbstractEOSRestService; import com.primeton.ext.annotation.rest.RestService; import com.primeton.ext.annotation.rest.HttpMethod; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // 注意:@RestService必须加在类上,且name与XML配置一致 @RestService( name = "creditLimit", path = "/api/v1/credit/limit", method = HttpMethod.GET ) public class CreditLimitRestService extends AbstractEOSRestService { @Override protected Object doExecute(HttpServletRequest request, HttpServletResponse response) throws Exception { // 1. 提取参数:支持query string和path variable两种方式 // 此处用query string,即GET /api/v1/credit/limit?customerId=123456 String customerId = request.getParameter("customerId"); // 2. 参数校验(业务逻辑前必做) if (customerId == null || customerId.trim().isEmpty()) { throw new IllegalArgumentException("customerId is required"); } // 3. 调用原始业务服务(此处简化为new,实际应从EOS容器获取) CreditLimitService bizService = new CreditLimitService(); CreditLimitResult result = bizService.getCreditLimit(customerId); // 4. EOS会自动将result对象序列化为JSON return result; } }同时,编写配套的业务类和结果类(模拟):
// CreditLimitService.java package com.bank.credit.service; public class CreditLimitService { public CreditLimitResult getCreditLimit(String customerId) { // 模拟数据库查询 CreditLimitResult result = new CreditLimitResult(); result.setCustomerId(customerId); result.setTotalLimit(500000.00); result.setUsedLimit(120000.00); result.setStatus("ACTIVE"); return result; } } // CreditLimitResult.java package com.bank.credit.dto; public class CreditLimitResult { private String customerId; private Double totalLimit; private Double usedLimit; private String status; // getter/setter省略 }4.3 编译打包:生成可部署的jar
使用Maven构建(pom.xml关键配置):
<dependencies> <!-- EOS7.5核心依赖 --> <dependency> <groupId>com.primeton</groupId> <artifactId>eos-platform</artifactId> <version>7.5.20210618</version> <scope>provided</scope> </dependency> <!-- REST扩展依赖 --> <dependency> <groupId>com.primeton</groupId> <artifactId>eos-platform-rest</artifactId> <version>7.5.20210618</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>执行编译:
mvn clean compile package # 生成target/credit-rest-service-1.0.jar4.4 部署服务:复制jar并配置XML
将生成的jar复制到EOS的deploy目录:
cp target/credit-rest-service-1.0.jar /opt/eos75/deploy/lib/创建服务配置文件:
mkdir -p /opt/eos75/deploy/services vi /opt/eos75/deploy/services/credit-limit-service.xml填入内容:
<?xml version="1.0" encoding="UTF-8"?> <service xmlns="http://www.primeton.com/schema/service" name="creditLimit" class="com.bank.credit.rest.CreditLimitRestService" scope="singleton"> <property name="timeout" value="30000"/> <property name="maxRetry" value="2"/> </service>4.5 启用JSON与跨域:修改rest-config.xml
编辑/opt/eos75/platform/conf/rest-config.xml,确保以下两部分已启用:
<!-- JSON序列化器 --> <json-serializer enabled="true" date-format="yyyy-MM-dd HH:mm:ss" include-nulls="false"/> <!-- 跨域响应头 --> <response-header> <header name="Access-Control-Allow-Origin" value="*"/> <header name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS"/> <header name="Access-Control-Allow-Headers" value="Content-Type,X-Requested-With"/> </response-header>4.6 重启并验证:用curl测试端点
重启WebLogic后,执行测试:
# 测试服务是否注册成功(返回服务列表JSON) curl -i http://localhost:7001/eos/rest/services # 测试具体接口(GET方式) curl -i "http://localhost:7001/eos/rest/api/v1/credit/limit?customerId=123456" # 预期返回: # HTTP/1.1 200 OK # Content-Type: application/json;charset=UTF-8 # {"customerId":"123456","totalLimit":500000.0,"usedLimit":120000.0,"status":"ACTIVE"}如果返回404,检查XML配置的name和@RestService的name是否一致;如果返回403,检查权限配置;如果返回500且日志报ClassNotFoundException,检查jar是否复制到正确的lib目录。
4.7 权限配置:通过控制台完成最后一步
打开浏览器,访问http://localhost:7001/eos/console,使用管理员账号登录。导航至【系统管理】→【服务管理】→【Restful服务】,在列表中找到creditLimit,点击右侧【权限配置】,勾选“系统管理员”角色,保存。此时,用管理员账号调用接口即可成功。
实操心得:权限配置后,无需重启服务,EOS实时生效。但如果修改了XML配置或Java类,必须重启WebLogic。我们总结了一个“三分钟排障口诀”:404看name,403看权限,500看日志,200看数据——只要HTTP状态码正确,数据不对就一定是业务逻辑问题,和Restful框架无关。
5. 常见问题与排查技巧实录:那些文档里不会写的坑
在十几个EOS7.5 Restful项目落地过程中,我们整理出一份高频问题清单。这些问题大多源于EOS7.5的设计哲学与现代Web开发习惯的冲突,文档里往往一笔带过,但实际踩中一个就能卡住整个交付。
5.1 问题:POST请求总是400 Bad Request,日志显示“Unsupported Media Type”
现象:前端用axios.post('/api/v1/credit/apply', {amount: 10000}),后端始终400,日志里没有详细错误。
根因:EOS7.5的Restful模块默认只接受Content-Type为application/x-www-form-urlencoded或text/plain的POST请求。application/json不被识别,因此拒绝解析。
解决方案:
- 前端改用URL编码提交:
// 不要这样 axios.post('/api/v1/credit/apply', {amount: 10000}); // 改成这样 const params = new URLSearchParams(); params.append('amount', '10000'); axios.post('/api/v1/credit/apply', params);- 或者,在doExecute方法中手动读取JSON:
@Override protected Object doExecute(HttpServletRequest request, HttpServletResponse response) throws Exception { // 读取原始JSON Body StringBuilder jsonBody = new StringBuilder(); BufferedReader reader = request.getReader(); String line; while ((line = reader.readLine()) != null) { jsonBody.append(line); } // 用Jackson解析 ObjectMapper mapper = new ObjectMapper(); ApplyRequest req = mapper.readValue(jsonBody.toString(), ApplyRequest.class); // 业务处理... return result; }注意:这种方式需要在pom.xml中添加jackson-databind依赖,且EOS7.5默认不提供,必须自行引入。
5.2 问题:日期字段序列化为时间戳(13位数字),而非期望的"yyyy-MM-dd HH:mm:ss"字符串
现象:返回JSON中,createDate字段是1623456789000,前端无法直接显示。
根因:EOS7.5的JSON序列化器默认将java.util.Date转为毫秒时间戳,不受rest-config.xml中date-format配置影响(该配置只对java.sql.Timestamp有效)。
解决方案:在实体类的Date字段上添加EOS自定义注解:
public class CreditLimitResult { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; // getter/setter }但注意,这需要引入jackson-annotations依赖,且EOS7.5的序列化器必须是Jackson(确认rest-config.xml中 的class属性为com.primeton.ext.serializer.jackson.JacksonJsonSerializer)。
5.3 问题:服务发布后,/eos/rest/services列表里看不到,但日志没有报错
现象:XML配置、Java类、jar包都确认无误,重启后列表为空。
排查步骤:
- 检查jar包是否在
/opt/eos75/deploy/lib/,而非/opt/eos75/platform/lib/(后者是平台级依赖,前者才是服务级); - 检查XML文件名是否以
.xml结尾,且文件编码为UTF-8(Windows记事本保存的XML常含BOM头,EOS无法解析); - 查看WebLogic日志中是否有
[EOS-SERVICE] Loading service from file: credit-limit-service.xml,如果没有,说明XML未被扫描到; - 确认
/opt/eos75/deploy/services/目录权限是否为WebLogic进程用户可读。
5.4 问题:同一个服务类,GET能通,POST返回405 Method Not Allowed
现象:@RestService(method = HttpMethod.POST)配置后,curl -X POST仍405。
根因:EOS7.5的Restful模块对HTTP方法的校验非常严格。如果XML配置中指定了<property name="method" value="POST"/>,而@RestService注解里没写method,或两者不一致,就会触发405。
解决方案:只用一种方式声明方法。推荐统一用@RestService注解,XML中不要重复声明method属性。如果必须用XML,删除注解中的method。
5.5 问题:中文返回乱码,JSON中显示"???"
现象:返回JSON包含中文字段名或值,但浏览器显示为方块或问号。
根因:EOS7.5的Restful响应默认字符集是ISO-8859-1,而非UTF-8。
解决方案:在/opt/eos75/platform/conf/rest-config.xml中,添加 节点:
<response-encoding> <charset>UTF-8</charset> </response-encoding>5.6 问题:服务调用耗时长,但日志里没有慢SQL,怀疑是EOS框架层瓶颈
现象:业务逻辑很简单(如查缓存),但接口平均响应2秒以上。
排查技巧:启用EOS的Restful详细日志。在/opt/eos75/platform/conf/log4j.properties中,添加:
log4j.logger.com.primeton.ext.rest=DEBUG log4j.logger.com.primeton.ext.serializer=DEBUG重启后,日志会打印出每个阶段耗时:[DEBUG] [EOS-REST] Start processing request for /api/v1/credit/limit[DEBUG] [EOS-REST] JSON serialization took 1200ms
如果序列化耗时长,说明返回对象过大或有循环引用(如Hibernate懒加载代理),需在实体类上加@JsonIgnore。
5.7 问题速查表
| 问题现象 | 最可能原因 | 快速验证命令 | 解决方案 |
|---|---|---|---|
| 404 Not Found | XML中name与@RestService不一致 | curl http://localhost:7001/eos/rest/services | grep creditLimit | 统一name值,区分大小写 |
| 403 Forbidden | 未在控制台配置权限 | SELECT * FROM t_rest_service_privilege WHERE service_name='creditLimit' | 控制台配置或执行INSERT SQL |
| 500 Internal Error | 类路径找不到(ClassNotFoundException) | ls -l /opt/eos75/deploy/lib/ | grep credit | 确认jar包存在且无版本冲突 |
| 返回XML而非JSON | rest-config.xml中 未启用 | grep "enabled" /opt/eos75/platform/conf/rest-config.xml | 设为enabled="true" |
| POST参数为空 | 前端Content-Type为application/json | curl -H "Content-Type: application/json" -X POST ... | 改用x-www-form-urlencoded或手动解析 |
最后分享一个小技巧:在开发阶段,把
/opt/eos75/platform/conf/rest-config.xml中的<debug>true</debug>设为true,EOS会在响应头中添加X-EOS-REST-Debug: true和详细的处理链路,对定位框架层问题极有帮助。上线前务必关掉,避免泄露内部信息。
6. 进阶思考:当Restful遇上EOS7.5的固有限制
做到上面几步,你已经能稳定对外提供Restful服务了。但真正的挑战在于:如何让这些服务满足现代API治理的要求?EOS7.5不是为API网关时代设计的,它的Restful能力是“够用就好”,很多我们认为理所当然的功能,它原生不支持。这时候,你需要在“用EOS的方式”和“满足业务需求”之间找平衡点。
6.1 分页、排序、过滤:没有@QueryMap,只有手写拼接
EOS7.5的Restful不支持Spring Data JPA那种@Query("from User u where u.status = :status")的动态查询。所有参数都得从request.getParameter()里手工拿,然后拼SQL。比如一个用户列表接口,前端传?page=1&size=10&sort=name,asc&filter=status:ACTIVE,你得在doExecute里解析这些参数,再组装成MyBatis的RowBounds和动态SQL。
我们的实践方案:
- 写一个通用的ParameterParser工具类,把query string解析为Map<String, Object>;
- 在业务DAO层,用MyBatis的 标签根据Map中的key动态生成WHERE条件;
- 分页用MyBatis的RowBounds,排序用ORDER BY ${sort}(注意SQL注入风险,必须白名单校验sort字段)。
注意:${sort}是MyBatis的OGNL表达式,直接拼入SQL,必须严格校验。我们维护了一个允许排序的字段白名单:
Arrays.asList("name", "createTime", "status"),不在名单内的sort值直接抛异常。
6.2 API文档:Swagger?不存在的,只能手写Markdown
EOS7.5没有集成Swagger或OpenAPI生成器。你不能指望/v3/api-docs这种端点。但我们发现一个取巧办法:利用EOS管理控制台的“服务测试”功能。在控制台中,每个Restful服务都有一个“测试”按钮,点击后会弹出表单,列出所有参数(来自request.getParameter的key)。把这个表单截图,配上curl示例,就是一份可用的API文档。
更进一步,我们用Python写了个小脚本,自动爬取/eos/rest/services返回的JSON,提取每个服务的name、path、method、description(如果XML里写了 ),生成Markdown文档。虽然简陋,但胜在准确——它反映的是真实部署的服务,不是开发时的设想。
6.3 版本管理:/v1/ vs /v2/,EOS不支持路径版本化
EOS7.5的@RestService.path是静态字符串,不支持正则或占位符。你想实现/api/v1/credit/limit和/api/v2/credit/limit两个版本,不能靠一个服务类搞定,必须写两个类:CreditLimitV1RestService和CreditLimitV2RestService,分别配置不同的path和name。
代价:业务逻辑重复。我们的做法是,把核心业务逻辑抽成一个独立的Service类(如CreditLimitCoreService),两个Rest服务类都调用它。这样,v1和v2的差异只在参数解析和结果包装上,业务逻辑零重复。
6.4 监控告警:没有Micrometer,但有日志埋点
EOS7.5不提供Prometheus指标端点,但它的日志足够丰富。我们在doExecute方法开头加日志:
long startTime = System.currentTimeMillis(); logger.info("Start REST call: {} with params {}", request.getRequestURI(), request.getQueryString());结尾加:
long duration = System.currentTimeMillis() - startTime; logger.info("End REST call: {} in {}ms", request.getRequestURI(), duration);然后用ELK(Elasticsearch + Logstash + Kibana)收集这些日志,就能做出响应时间P95、错误率、QPS等核心API指标看板。运维同学只需要学会看Kibana,不需要学新监控体系。
6.5 安全加固:除了权限,还要防刷、防爬
EOS7.5的权限是粗粒度的(服务级),不支持接口级限流(如每分钟100次)。我们采用“组合拳”:
- Nginx层限流:在EOS前面加Nginx,用limit_req模块限制单IP请求频率;
- 业务层校验:在doExecute中,用Redis记录用户最近10次调用时间,计算间隔,超频