我正在尝试对 Blackbaud CRM 进行 API 调用,以获取包含选民电子邮件地址的数据列表。我已将 SOAP 请求格式化为 jinja2 模板,并且我正在使用 python 和 requests 进行调用。这是 SOAP 请求的规范:
POST /1234ABC_fa123b46-12a4-4119-a334-5678e2e59d29/appfxwebservice.asmx HTTP/1.1
Host: bbisec04pro.blackbaudhosting.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "Blackbaud.AppFx.WebService.API.1/DataListLoad"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<DataListLoadRequest xmlns="Blackbaud.AppFx.WebService.API.1">
<DataListID>guid</DataListID>
<DataListName>string</DataListName>
<ContextRecordID>string</ContextRecordID>
<Parameters>
<Values xmlns="bb_appfx_dataforms">
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
</Values>
</Parameters>
<MaxRows>int</MaxRows>
<MaxTotalRecords>int</MaxTotalRecords>
<ViewFormID>guid</ViewFormID>
<RecordIDColumn>string</RecordIDColumn>
<IncludeMetaData>boolean</IncludeMetaData>
<SecurityContext>
<SecurityFeatureID>guid</SecurityFeatureID>
<SecurityFeatureType>None or Form or DataList or RecordOperation or BusinessProcess or Dashboard or SearchList or SimpleDataList or Task or SmartQuery or AdHocQueryView or BatchType or AddCodeTableEntry or UpdateCodeTableEntry or DeleteCodeTableEntry or Batch or BatchTemplate or Kpi or MergeTask or SmartField or GlobalChange or ReportParameter or SystemPrivilege or ConfigurationData or BatchTemplateCustomize or BatchProcessor or Page or MapEntity or ExportDefinition</SecurityFeatureType>
<RecordContext>
<RecordID>string</RecordID>
<RecordType>string</RecordType>
</RecordContext>
<AttributeContext>
<AttributeCategoryID>string</AttributeCategoryID>
</AttributeContext>
</SecurityContext>
<IgnoreInvalidFilters>boolean</IgnoreInvalidFilters>
<ResultsAsXml>boolean</ResultsAsXml>
<UserSettingsPath>string</UserSettingsPath>
<SortFieldID>string</SortFieldID>
<SortDirection>Ascending or Descending</SortDirection>
<StartRowIndex>int</StartRowIndex>
<RecordToInclude>string</RecordToInclude>
<RowRangeKeyToRemove>string</RowRangeKeyToRemove>
<CancelID>string</CancelID>
<IgnoreExtraFields>boolean</IgnoreExtraFields>
</DataListLoadRequest>
</soap:Body>
</soap:Envelope>
这是我的 jinja2 模板:
{% extends 'client_app.xml' %}
{% block request %}<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<DataListLoadRequest xmlns="Blackbaud.AppFx.WebService.API.1">
{% block client_app %}{{ super() }}{% endblock %}
{% if guid %}<DataListID>{{ guid }}</DataListID>{% endif %}
{% if name %}<DataListName>{{ name }}</DataListName>{% endif %}
<ContextRecordID>{{ lookup_id }}</ContextRecordID>
{% if parameters %}<Parameters>
<Values xmlns="bb_appfx_dataforms">
{% for item in parameters %}
<fv ID="{{ item['id'] }}">
<Value />
<ValueTranslation>{{ item['translation'] }}</ValueTranslation>
</fv>
{% endfor %}
</Values>
</Parameters>{% endif %}
{% if max_rows %}<MaxRows>{{ max_rows }}</MaxRows>{% endif %}
{% if max_total %}<MaxTotalRecords>{{ max_total }}</MaxTotalRecords>{% endif %}
{% if view_id %}<ViewFormID>{{ view_id }}</ViewFormID>{% endif %}
{% if record_id_col %}<RecordIDColumn>{{ record_id_col }}</RecordIDColumn>{% endif %}
{% if has_metadata %}<IncludeMetaData>{{ has_metadata }}</IncludeMetaData>{% endif %}
{% if security_context %}<SecurityContext>
{% if security_context['guid'] %}<SecurityFeatureID>{{ security_context['guid'] }}</SecurityFeatureID>{% endif %}
{% if security_context['feature_type'] %}<SecurityFeatureType>{{ security_context['feature_type'] }}</SecurityFeatureType>{% endif %}
{% if security_context['record'] %}<RecordContext>
<RecordID>{{ security_context['record']['id'] }}</RecordID>
<RecordType>{{ security_context['record']['type'] }}</RecordType>
</RecordContext>{% endif %}
{% if attribute_context %}<AttributeContext>
<AttributeCategoryID>{{ attribute_context}}</AttributeCategoryID>
</AttributeContext>{% endif %}
</SecurityContext>{% endif %}
{% if ignore_invalids %}<IgnoreInvalidFilters>{{ ignore_invalids }}</IgnoreInvalidFilters>{% endif %}
<ResultsAsXml>{{ xml_result }}</ResultsAsXml>
{% if settings_path %}<UserSettingsPath>{{ settings_path }}</UserSettingsPath>{% endif %}
{% if sort_id %}<SortFieldID>{{ sort_id }}</SortFieldID>{% endif %}
{% if sort_direction %}<SortDirection>{{ sort_direction }}</SortDirection>{% endif %}
{% if start %}<StartRowIndex>{{ start }}</StartRowIndex>{% endif %}
{% if rec_to_include %}<RecordToInclude>{{ rec_to_include }}</RecordToInclude>{% endif %}
{% if to_remove %}<RowRangeKeyToRemove>{{ to_remove }}</RowRangeKeyToRemove>{% endif %}
{% if cancel_id %}<CancelID>{{ cancel_id }}</CancelID>{% endif %}
{% if ignore_extra %}<IgnoreExtraFields>{{ ignore_extra }}</IgnoreExtraFields>{% endif %}
</DataListLoadRequest>
</soap:Body>
</soap:Envelope>
{% endblock %}
这是渲染模板的代码:
def render_data_list(
guid='',
name='',
lookup_id='',
parameters='',
max_rows='',
max_total='',
view_id='',
record_id_col='',
has_metadata='',
security_context='',
ignore_invalids='',
xml_result=0,
settings_path='',
sort_id='',
sort_direction='',
start='',
rec_to_include='',
to_remove='',
cancel_id='',
ignore_extra='',
):
template = env.get_template('data_list_load.xml')
return template.render(
guid=guid,
name=name,
lookup_id=lookup_id,
parameters=parameters,
max_rows=max_rows,
max_total=max_total,
view_id=view_id,
record_id_col=record_id_col,
has_metadata=has_metadata,
security_context=security_context,
ignore_invalids=ignore_invalids,
xml_result=xml_result,
settings_path=settings_path,
sort_id=sort_id,
sort_direction=sort_direction,
start=start,
rec_to_include=rec_to_include,
to_remove=to_remove,
cancel_id=cancel_id,
ignore_extra=ignore_extra,
)
这是实际进行调用的函数:
def get_list(guid='', name='', lookup_id=''):
endpoint = 'Blackbaud.AppFx.WebService.API.1/DataListLoad'
header = config.default_head.copy()
header.update({'SOAPAction': endpoint})
body = render_data_list(guid=guid, name=name, lookup_id=lookup_id)
res = config.session.post(
f'{config.base_url}{config.db_id}{config.api}',
data=body,
headers=header,
)
print(res.status_code, res.text)
这是带有参数的实际 API 调用:
if __name__ == '__main__':
print(get_list(guid=config.email_list_guid, lookup_id=12345678))
我收到一条错误消息,提示“无法加载数据列表。将数据类型 nvarchar 转换为 uniqueidentifier 时出错。” 我尝试将字符串传递给,lookup_id
但仍然收到相同的错误。我试图<ContextRecordID>
从模板中删除标签,但这些标签似乎是必需的。我使用来自 Blackbaud CRM 的成分查找 ID 作为 ContextRecordID,因为此数据列表的 ContextRecordType 是成分。我应该使用其他东西作为 ContextRecordID,还是我的查找 ID 的格式不正确?对此的任何帮助将不胜感激。这是完整的错误响应:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Header><ResponseErrorHeader xmlns="Blackbaud.AppFx.WebService.API.1"><Name>DataListLoad</Name><ErrorCode>GeneralError</ErrorCode><ErrorText>Unable to load data list. Error converting data type nvarchar to uniqueidentifier.</ErrorText><ExceptionDetails>Blackbaud.AppFx.Server.ServiceException: Unable to load data list. Error converting data type nvarchar to uniqueidentifier. ---> System.Data.SqlClient.SqlException: Error converting data type nvarchar to uniqueidentifier.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader()
at System.Data.SqlClient.SqlCommand.InternalEndExecuteReader(IAsyncResult asyncResult, String endMethod)
at System.Data.SqlClient.SqlCommand.EndExecuteReaderInternal(IAsyncResult asyncResult)
at System.Data.SqlClient.SqlCommand.EndExecuteReader(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.AsyncSupport.CancellableSqlCommandHelper.EndExecuteReader(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.DataListLoadProcessor.EndProcessRequest(IAsyncResult asyncResult)
--- End of inner exception stack trace ---
at Blackbaud.AppFx.Server.DataListLoadProcessor.EndProcessRequest(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.AsyncRequestProcessor`2.EndGetReply(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.AppService.EndDispatchRequest(IAsyncResult asyncResult)</ExceptionDetails><RootRequestName>DataListLoadRequest</RootRequestName><RequestProcessorName>Blackbaud.AppFx.Server.DataListLoadProcessor</RequestProcessorName></ResponseErrorHeader></soap:Header><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Server was unable to process request. ---> Unable to load data list. Error converting data type nvarchar to uniqueidentifier. ---> Error converting data type nvarchar to uniqueidentifier.</faultstring><detail /></soap:Fault></soap:Body></soap:Envelope>