gsoap入门:获取服务器(axis2)端的异常(exception)对象

我们的webservice服务器端是以axis2为基础构建的,客户端提交的soap请求参数不正确或其他情况下,会抛出异常给客户端。这些异常在SOAP这一层是以Fault类型的消息呈现的,比如目前有如下异常:

DeleteImgFail // 图像文件删除失败
DuplicateReord // 数据库记录重复
ImageError // 图像读写或格式错误
NotFaceDetected // 没有检测到人脸
NotFoundBean // 没有数据库找到记录
ServiceRuntime // 服务端的运行时异常(java.lang.RuntimeException)

比如本文示例的detectFace调用的服务器端的java代码实现如下(代码中声明会抛出ImageError,ServiceRuntime异常):

    /**
     * 参见{@link FaceAPILocal#detectFace(byte[],FRect)}<br>
     * @param imgData
     * @param detectRectangle
     * @return CodeInfo[]
     * @throws ImageError
     * @see FaceAPILocal#detectFace(byte[],FRect)
     */
    public final CodeInfo[] sdk_detectFace (byte[] imgData,FRect detectRectangle) throws ImageError,ServiceRuntime{
        try{
            return _faceAPIInstance.detectFace(imgData,detectRectangle);
        }catch (RuntimeException e) {
            throw new ServiceRuntime(e);
        }
    }

当客户端调用webservice出现异常时,需要能获取异常类型然后根据需要做相应的处理。这就需要当soap调用失败时,不仅能输出faultstring,还要知道异常类型。

于是仔细研究了gsoap的异常处理 《SOAP Fault Processing》
下面的代码摘自gosap官网手册,是gosap的异常数据结构,根据官网的说明SOAP_ENV__Fault 中前面4个字段用于SOAP 1.1,后面5个字段用于SOAP 1.2。不论是SOAP 1.1还是SOAP 1.2都有一个类型为SOAP_ENV__Detail*的字段。

struct SOAP_ENV__Fault 
{ 
   _QName faultcode; // _QName is builtin 
   char *faultstring; 
   char *faultactor; 
   struct SOAP_ENV__Detail *detail; 
   struct SOAP_ENV__Code *SOAP_ENV__Code; // MUST be a SOAP_ENV__Code struct defined below 
   char *SOAP_ENV__Reason; 
   char *SOAP_ENV__Node; 
   char *SOAP_ENV__Role; 
   struct SOAP_ENV__Detail *SOAP_ENV__Detail; // SOAP 1.2 detail field 
}; 

这是官网上SOAP_ENV__Detail结构的说明,很简单,我推测这应该是通用版本。

struct SOAP_ENV__Detail 
{ 
   int __type; // The SOAP_TYPE_ of the object serialized as Fault detail 
   void *fault; // pointer to the fault object, or NULL 
   char *__any; // any other detail element content (stored in XML format) 
};

我生的代码中SOAP_ENV__Detail实际定义如下:

/* facedbservice.h:4949 */
#ifndef WITH_NOGLOBAL
#ifndef SOAP_TYPE_SOAP_ENV__Detail
#define SOAP_TYPE_SOAP_ENV__Detail (215)
/* SOAP_ENV__Detail: */
struct SOAP_ENV__Detail {
      public:
        /** Optional element 'ns1:FaceDbServiceDeleteImgFail' of XSD type 'ns1:FaceDbServiceDeleteImgFail' */
        _ns1__FaceDbServiceDeleteImgFail *ns1__FaceDbServiceDeleteImgFail;
        /** Optional element 'ns1:FaceDbServiceDuplicateReord' of XSD type 'ns1:FaceDbServiceDuplicateReord' */
        _ns1__FaceDbServiceDuplicateReord *ns1__FaceDbServiceDuplicateReord;
        /** Optional element 'ns1:FaceDbServiceImageError' of XSD type 'ns1:FaceDbServiceImageError' */
        _ns1__FaceDbServiceImageError *ns1__FaceDbServiceImageError;
        /** Optional element 'ns1:FaceDbServiceNotFaceDetected' of XSD type 'ns1:FaceDbServiceNotFaceDetected' */
        _ns1__FaceDbServiceNotFaceDetected *ns1__FaceDbServiceNotFaceDetected;
        /** Optional element 'ns1:FaceDbServiceNotFoundBean' of XSD type 'ns1:FaceDbServiceNotFoundBean' */
        _ns1__FaceDbServiceNotFoundBean *ns1__FaceDbServiceNotFoundBean;
        /** Optional element 'ns1:FaceDbServiceServiceRuntime' of XSD type 'ns1:FaceDbServiceServiceRuntime' */
        _ns1__FaceDbServiceServiceRuntime *ns1__FaceDbServiceServiceRuntime;
        /** Any type of element 'fault' assigned to fault with its SOAP_TYPE_T assigned to __type */
        /** Do not create a cyclic data structure throught this member unless SOAP encoding or SOAP_XML_GRAPH are used for id-ref serialization */
        int __type;
        void *fault;
        char *__any;
      public:
        /** Return unique type id SOAP_TYPE_SOAP_ENV__Detail */
        int soap_type() const { return SOAP_TYPE_SOAP_ENV__Detail; }
        /** Constructor with member initializations */
        SOAP_ENV__Detail()
        {
          ns1__FaceDbServiceDeleteImgFail = (_ns1__FaceDbServiceDeleteImgFail *)0;
          ns1__FaceDbServiceDuplicateReord = (_ns1__FaceDbServiceDuplicateReord *)0;
          ns1__FaceDbServiceImageError = (_ns1__FaceDbServiceImageError *)0;
          ns1__FaceDbServiceNotFaceDetected = (_ns1__FaceDbServiceNotFaceDetected *)0;
          ns1__FaceDbServiceNotFoundBean = (_ns1__FaceDbServiceNotFoundBean *)0;
          ns1__FaceDbServiceServiceRuntime = (_ns1__FaceDbServiceServiceRuntime *)0;
          __type = 0;
          fault = NULL;
          __any = (char *)0;
        }
        /** Friend allocator used by soap_new_SOAP_ENV__Detail(struct soap*, int) */
        friend SOAP_FMAC1 SOAP_ENV__Detail * SOAP_FMAC2 facedbservice_instantiate_SOAP_ENV__Detail(struct soap*, int, const char*, const char*, size_t*);
};
#endif
#endif

对比官网手册上的代码就发现实际的SOAP_ENV__Detail结构中多了几个成员变量:

ns1__FaceDbServiceDeleteImgFail
ns1__FaceDbServiceDuplicateReord
ns1__FaceDbServiceImageError
ns1__FaceDbServiceNotFaceDetected
ns1__FaceDbServiceNotFoundBean
ns1__FaceDbServiceServiceRuntime

而这多出来的成员变量正好与前面我的服务器端抛出的异常对应。

当我尝试让服务器端抛出ServerRuntime异常时,SOAP_ENV__Detail结构中对应的ns1__FaceDbServiceServiceRuntime成员变量被赋值了一个异常类型对象,其他变量都为null,这正是我要的结果.
这里写图片描述

搞清楚原理,就可以对服务器端异常类型进行判断和处理,下面是代码示例.
test_gsoap.cpp

#include <sstream>
#include "FaceDbServiceSoap11Binding.nsmap"
#include "file_utilits.h"
#include "stdlib.h"
// 根据soap.version判断SOAP1.1或SOAP1.2版本,返回不同的字段
SOAP_ENV__Detail* getFaultDetail(soap &soap) {
	return  soap.version == 2 ? soap.fault->SOAP_ENV__Detail : soap.fault->detail;
}
int main(int argc, char * argv[]) {
	soap soap;
	// 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();
	// 原本这里应该将设置b为图像数据,但这里故意删除,不给imgData赋值(为null),以便让服务器抛出ServerRuntime异常。
	//param.imgData = &b;
	
	soap_call___ns1__sdk_USCOREdetectFace(&soap,"http://gdface.wicp.net:15865/axis2/services/FaceDbService",nullptr, &param,ret);
	if (soap.error!= SOAP_OK) {
		soap_fault(&soap);
		// 获取SOAP_ENV__Detail对象指针
		auto detail = getFaultDetail(soap);
		if (detail) {
			// 根据每个异常对应的字段是否为null判断异常类型做相应的处理
			if (detail->ns1__FaceDbServiceDeleteImgFail) {
				cout << "FaceDbServiceDeleteImgFail:"<< *soap_faultdetail(&soap) << endl;
			}else if (detail->ns1__FaceDbServiceDuplicateReord) {
				cout << *detail->ns1__FaceDbServiceDuplicateReord->DuplicateReord->message << endl;
			}else if (detail->ns1__FaceDbServiceImageError) {
				cout << "FaceDbServiceImageError:"<< *soap_faultdetail(&soap) << endl;
			}else if (detail->ns1__FaceDbServiceNotFaceDetected) {
				cout << "FaceDbServiceNotFaceDetected:"<< *soap_faultdetail(&soap) << endl;
			}else if (detail->ns1__FaceDbServiceNotFoundBean) {
				cout << "FaceDbServiceNotFoundBean:"<< *soap_faultdetail(&soap) << endl;
			}else if (detail->ns1__FaceDbServiceServiceRuntime) {
				// 输出服务器端的堆栈信息
				cout << *detail->ns1__FaceDbServiceServiceRuntime->ServiceRuntime->serverStackTraceMessage << endl;
			}else {
				// 如果非服务器端代码抛出异常则调用soap_faultdetail显示错误信息。
				cout << *soap_faultdetail(&soap) << std::endl;
			}
		}
	}else {
		if(ret.return_.size())
			for (auto code : ret.return_) {
				std::printf("face rect = (%d,%d,%d,%d)\n", *(code->pos->left), *(code->pos->top), *(code->pos->width), *(code->pos->height));
			}
		else
			cout << "NotFaceDetected" << endl;
	}
	soap_done(&soap);
}

如下图,输出了服务器端的堆栈信息
这里写图片描述
注意:

如果使用这种方法判断异常类型,在代码生成的时候,就不能用-qname指定C++namespace

生成代码的脚本如下:

wsdl2h -o facedbservice.h http://gdface.wicp.net:15865/axis2/services/FaceDbService?wsdl
soapcpp2 -C -L -x -qfacedbservice -IJ:\gsoap-2.8\gsoap\import facedbservice.h
-C指定只生成client端代码
-q参数指定生成代码的文件前缀<prefix>,如果不指定默认前缀为soap
-L指定不生成 <prefix>ClientLib.cpp文件

然后生成如下文件:
这里写图片描述

将.cpp文件和$GSOAP\gsoap\stdsoap2.cpp加入工程,就可以编译了。
这里写图片描述

参考资料
《SOAP Fault Processing》 http://www.genivia.com/doc/soapdoc2.html#tth_sEc12

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

10km

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

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

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

打赏作者

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

抵扣说明:

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

余额充值