gsoap入门:解决axis2服务器返回错误:com.ctc.wstx.exc.WstxParsingException: Undeclared namespace prefix “ns1“

gSOAP C++ 客户端问题解决
本文介绍了一种使用gSOAP开发C++客户端调用WebService时遇到的问题及解决办法。主要问题是由于未正确配置命名空间而导致的SOAP请求格式错误。

我们的项目中webservice服务器已经搭建好,基于用axis2提供名为FaceDbService的人脸识别服务。现在的任务是用gosap开发C++版本的客户端,所以最近在尝试用gsoap生成的c++代码来调用webservice.

关于生成gsoap C++客户端代码及编译,参见我的前一篇博客《gsoap入门:C/C++代码生成及编译》

测试代码

下面是测试代码

#include <sstream>
#include "FaceDbServiceSoap11Binding.nsmap"
#include "file_utilits.h"
#include "stdlib.h"
#include "plugin.h"
using namespace facedbservice;
int main(int argc, char * argv[]) {
	struct soap soap;
	// 对象初始化
	soap_init(&soap);
	// 注册plugin插件,用于显示request/response xml文本用于调试
	soap_register_plugin(&soap, plugin);
	// RPC调用参数对象
	_ns1__sdk_USCOREdetectFace param;
	// RPC返回对象
	_ns1__sdk_USCOREdetectFaceResponse ret;
	// 加载一张图像数据到内存(字节数组std::vector<uint8_t>)
	auto img_file= gdface::load_binary_file("d:\\tmp\\he049.jpg");
	// 将字节数组转为xsd__base64Binary对象,并设置为imgData参数
	xsd__base64Binary b;
	b.soap = &soap;
	b.__ptr = img_file.data();
	b.__size = img_file.size();
	param.imgData = &b;
	// 执行soap调用detectFace
	soap_call___ns1__sdk_USCOREdetectFace(&soap,"http://gdface.wicp.net:15865/axis2/services/FaceDbService",nullptr, &param,ret);
	// 检查错误
	if (soap.error!= SOAP_OK) {
		// 显示错误信息
		soap_stream_fault(&soap, std::cerr);
	}else {
		// 调用成功则输出返回的数据
		for (auto code : ret.return_) {
			std::printf("soap err,errcode = (%d,%d,%d,%d)\n", *(code->pos->left), *(code->pos->top), *(code->pos->width), *(code->pos->height));
		}
	}
	soap_done(&soap);
}

错误信息

程序执行时出错了:

/////////plugin插件输出的 request xml///////////////////////////////////
POST /axis2/services/FaceDbService HTTP/1.1
Host: 192.168.1.18:8080
User-Agent: gSOAP/2.8
Content-Type: text/xml; charset=utf-8
Content-Length: 8583
Connection: close
SOAPAction: "urn:sdk_detectFace"

<?xml version="1.0" encoding="UTF-8"?>
<ns:sdk_detectFace><ns:imgData>
//...图像数据
</ns:imgData></ns:sdk_detectFace>


HTTP/1.1 500 Int
ernal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Sat, 13 Aug 2016 02:55:08 GMT
Connection: close
/////////plugin插件输出的 response xml///////////////////////////////////

205
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://sc
hemas.xmlsoap.org/soap/envelope/"><soapenv:Header xmlns:wsa="http://www.w3.org/2
005/08/addressing"><wsa:Action>http://www.w3.org/2005/08/addressing/soap/fault</
wsa:Action></soapenv:Header><soapenv:Body><soapenv:Fault><faultcode></faultcode>
<faultstring>com.ctc.wstx.exc.WstxParsingException: Undeclared namespace prefix
"ns1"
 at [row,col {unknown-source}]: [2,19]</faultstring><detail /></soapenv:Fault></
soapenv:Body></soapenv:Envelope>

///////// 上面代码中soap_stream_fault()输出///////
Error 500 fault at source[no subcode]
"Error 500: HTTP 500 Internal Server Error"
Detail: [no detail]

原因分析

总是返回Undeclared namespace prefix "ns"
仔细查看request xml输出发现这个xml居然没有SOAP-ENV:Envelopetag,这不是个标准的SOAP格式啊。
试了各种 关键字在google/baidu上搜索,最后用"gsoap not SOAP-ENV:Envelope"用google在stackoverflow上找到一个类似的情况《Breaking changes in gsoap (updating version from 2.8.8 to 2.8.16)》

这里写图片描述
其中一个答案,给了我提醒 :
这里写图片描述
貌似是因为soap.namespaces变量为NULL造成的,再运行程序证实了这个猜测,soap.namespaces果然为NULL;
于是尝试在soap对象创建后,添加如下代码,问题解决。

if(!soap->namespaces)
	soap->namespaces=namespaces;
	// namespaces变量定义在#include "FaceDbServiceSoap11Binding.nsmap"

解决方案

虽然这样解决了问题,但还是不知道为什么呀,网上其他的教程中没有提到要手工设置soap.namespaces变量呀
于是想到跟踪soap对象的代码,看看namespaces变量是怎么被初始化的。找到了这里(soap_versioning(soap_init)(struct soap *soap, soap_mode imode, soap_mode omode)函数中),看到这里瞬间明白了。:
这里写图片描述

因为我在用gsoapcpp2生成gosap C++客户端代码的时候使用了-DWITH_NONAMESPACES编译stdsoap2.cpp文件,所以这里初始化soap对象时就将namespaces指针赋值为NULL。
这里写图片描述

再进一步研究gsoap的手册关于编译参数的控制参见《9.11 Library Build Flags》,找到WITH_NONAMESPACES的说明(下图红框部分)
这里写图片描述

这里说明,如果使用了-DWITH_NONAMESPACES定义,就必须显式的使用soap_set_namespaces为soap对象指定namespace。

到此,问题基本算是搞清楚了,导致soap请求数据格式错误的直接原因是soap初始化时没有指定namespace

从上面的分析来看,解决这个问题,有两个方案:

解决方案1

去掉编译stdsoap2.cpp文件设置的-DWITH_NONAMESPACES宏定义
这个方法最简单,但在multi-client and multi-server builds模式下并不适合,因为这种模式下必须使用-DWITH_NONAMESPACES宏定义,参见《9.1 soapcpp2 Options》
这里写图片描述

解决方案2

在soap对象初始化后用soap_set_namespaces函数为soap对象指定namespace。
这个方法也不复杂,只是添加一行代码,前面的测试代码修改如下:

#include <sstream>
#include "FaceDbServiceSoap11Binding.nsmap"
#include "file_utilits.h"
#include "stdlib.h"
#include "plugin.h"
using namespace facedbservice;
int main(int argc, char * argv[]) {
	soap soap;
	// 对象初始化
	// soap_init(&soap);//C++下不需要,因为soap的构造函数已经执行过soap_init
	// 设置namespaces
    // namespaces变量定义在#include "FaceDbServiceSoap11Binding.nsmap"      
    soap_set_namespaces(&soap, namespaces);

	// 下面省略的代码与以前相同
	.....
	//soap_done(&soap);//C++下不需要,因为soap的析构函数会执行soap_done
}
<think>好的,用户的问题是关于如何调整TnPM中的COMMON CODE PREFIX设置。首先,我需要明确TnPM是什么。TnPM通常指的是全面规范化生产维护(Total Normalized Productive Maintenance),是TPM(Total Productive Maintenance)的一种扩展,主要应用于设备管理。不过,用户提到的COMMON CODE PREFIX可能涉及到系统中的代码前缀配置,这可能与设备编码、分类或层级结构有关。 接下来,我需要回想用户提供的引用内容。引用[1]提到了Node.js模块的查找机制,引用[2]是关于npm安装和Vue-Cli的配置,引用[3]则是关于状态设计的建议。这些引用似乎与用户的问题关联不大,但可能需要注意到用户可能是在开发或配置某个系统时遇到COMMON CODE PREFIX的设置问题,可能涉及到模块化或配置文件的调整。 用户的问题具体是调整COMMON CODE PREFIX的配置方法或方式。首先,我需要考虑COMMON CODE PREFIX在TnPM系统中的具体应用场景。通常,这类前缀可能用于设备编码、工单编号或其他标识符的生成规则中。例如,不同的设备类型可能有不同的前缀,以便于分类和管理。 接下来,可能的配置方法包括修改配置文件、通过管理界面调整或使用特定的命令行工具。根据引用[1]中提到的模块查找机制,如果TnPM是基于Node.js开发的,可能需要查看相关模块的配置文件,如package.json或其他自定义配置文件。但TnPM通常是企业级应用,可能使用不同的技术栈,因此需要更通用的方法。 可能的步骤包括: 1. 访问系统管理界面,查找编码规则或系统配置部分。 2. 在配置文件中调整相关参数,可能需要编辑XML、JSON或数据库中的设置。 3. 使用命令行工具或API进行配置更新。 4. 重启服务或应用使配置生效。 此外,用户可能需要考虑权限问题,是否有足够的权限修改系统级配置,以及修改后的影响范围,比如是否会影响现有数据的引用。测试环境先行验证也是一个重要步骤。 总结来说,用户需要具体的配置路径和方法,但由于缺乏具体的系统文档,可能需要结合常见的TnPM实施案例进行推测,并建议用户查阅官方文档或联系支持团队获取准确信息。</think>关于TnPM系统中COMMON CODE PREFIX的配置调整,根据设备管理系统的典型实现方式,建议按以下步骤操作: ### 配置方法 1. **层级结构定位** - 进入设备树配置模块:`系统设置 > 编码规则 > 设备分类前缀` - 通过$$ \text{编码层级} = \frac{\text{设备类型标识}}{\text{位置标识}} $$公式定义编码结构[^3] 2. **参数修改方式** ```xml <!-- 示例:配置文件片段 --> <common_code> <prefix type="EQUIPMENT">EQP-${YYYY}-</prefix> <prefix type="LOCATION">LOC-${DEPT_CODE}-</prefix> </common_code> ``` 支持通配符:`${YYYY}`(年份)、`${SEQ}`(序列号)等 3. **数据库直接配置**(需DBA权限) ```sql UPDATE t_sys_code_rule SET prefix_value = 'MACH-' WHERE code_type = 'EQUIPMENT_CATEGORY'; ``` ### 注意事项 1. 修改后需重新生成编码映射表 2. 历史数据需进行编码迁移验证 3. 生产环境修改前应完成: - 配置备份(参考npm包管理机制[^1]) - 沙箱环境测试 - 影响范围分析
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值