Calling a secured webservice in Sophis Risque
Nov
30
Written by:
11/30/2012 2:37 PM
On the server side, several types of server, agreed in most cases by the Corporate architect, are used:
- IIS + .Net
- Websphere + Java
- ....
Depending of the type of webservers, the implementation in Sophis Risque, in C++, can be done easily or not. This post will describe the development done to access a secured webservice deployed on a Websphere webservice.
We tried to access to this webservice using managed C++. The problem resides on the fact that managed C++ is not using the .Net framework to access to the webservices, but the ATL SOAP instead. And since ATL SOAP is not maintained by Microsoft, we got a problem of connection on a secured (https) webservice developped in Java.
Thus, one of the solutions which was retained is to use the .Net framework in C#, exporting some class by making an assembly, and accessing it using the C++ managed. The development in Sophis Risque is still C++ native and calls a standard C API, developped in managed C++ and exporting these functions.
In Visual Studio VS2010, we used the class library C# wizard in order to create the C# assembly. Once the project created, in order to generate the proxy to the secured http server, just choose a "Add a service reference" when right-clicking on the project:
The following dialog should appear. Just type the address of the secured webservice and click on GO:
Once the proxy created, it can be used in some .Net 4.0 C# code as follow:
01.
using
System;
02.
using
System.Collections.Generic;
03.
using
System.Linq;
04.
using
System.Text;
05.
06.
namespace
ReportingParty
07.
{
08.
public
class
WebServiceCall
09.
{
10.
public
static
bool
GenerateUSI(
long
entityId,
ref
string
usi)
11.
{
12.
bool
_result =
false
;
13.
usi =
""
;
14.
WebService.GenerateUSIInput[] _input =
new
WebService.GenerateUSIInput[1];
15.
16.
_input[0] =
new
WebService.GenerateUSIInput();
17.
_input[0].entityId =
new
WebService.ThirdParty();
18.
_input[0].entityId.Code = entityId.ToString();
19.
_input[0].entityId.RepositoryId = WebService.Repository.SOPHIS;
20.
21.
WebService.CommonsClient _client =
new
WebService.CommonsClient(
"CommonsSoap11"
,
"https://myserver.fr:myport/rpservice_DEV/CommonService"
);
22.
23.
try
24.
{
25.
WebService.GenerateUSIOutput[] _output = _client.GenerateUSI(_input);
26.
if
(_output.Length > 0 && !_output[0].isError)
27.
{
28.
usi = _output[0].usi;
29.
_result =
true
;
30.
}
31.
}
32.
catch
(Exception e)
33.
{
34.
}
35.
return
_result;
36.
}
37.
}
38.
}
The "CommonsSoap11" binding mentioned above at line 21 corrresponds to the entry generated during the generation of the proxy into the app.config, which should look like this one:
01.
version
=
"1.0"
encoding
=
"utf-8"
?>
02.
<
configuration
>
03.
<
system.serviceModel
>
04.
<
bindings
>
05.
<
basicHttpBinding
>
06.
<
binding
name
=
"CommonsSoap11"
closeTimeout
=
"00:01:00"
openTimeout
=
"00:01:00"
07.
receiveTimeout
=
"00:10:00"
sendTimeout
=
"00:01:00"
allowCookies
=
"false"
08.
bypassProxyOnLocal
=
"false"
hostNameComparisonMode
=
"StrongWildcard"
09.
maxBufferSize
=
"65536"
maxBufferPoolSize
=
"524288"
maxReceivedMessageSize
=
"65536"
10.
messageEncoding
=
"Text"
textEncoding
=
"utf-8"
transferMode
=
"Buffered"
11.
useDefaultWebProxy
=
"true"
>
12.
<
readerQuotas
maxDepth
=
"32"
maxStringContentLength
=
"8192"
maxArrayLength
=
"16384"
13.
maxBytesPerRead
=
"4096"
maxNameTableCharCount
=
"16384"
/>
14.
<
security
mode
=
"Transport"
>
15.
<
transport
clientCredentialType
=
"None"
proxyCredentialType
=
"None"
16.
realm
=
""
/>
17.
<
message
clientCredentialType
=
"UserName"
algorithmSuite
=
"Default"
/>
18.
>
19.
>
20.
<
binding
name
=
"CommonsSoap111"
closeTimeout
=
"00:01:00"
openTimeout
=
"00:01:00"
21.
receiveTimeout
=
"00:10:00"
sendTimeout
=
"00:01:00"
allowCookies
=
"false"
22.
bypassProxyOnLocal
=
"false"
hostNameComparisonMode
=
"StrongWildcard"
23.
maxBufferSize
=
"65536"
maxBufferPoolSize
=
"524288"
maxReceivedMessageSize
=
"65536"
24.
messageEncoding
=
"Text"
textEncoding
=
"utf-8"
transferMode
=
"Buffered"
25.
useDefaultWebProxy
=
"true"
>
26.
<
readerQuotas
maxDepth
=
"32"
maxStringContentLength
=
"8192"
maxArrayLength
=
"16384"
27.
maxBytesPerRead
=
"4096"
maxNameTableCharCount
=
"16384"
/>
28.
<
security
mode
=
"None"
>
29.
<
transport
clientCredentialType
=
"None"
proxyCredentialType
=
"None"
30.
realm
=
""
/>
31.
<
message
clientCredentialType
=
"UserName"
algorithmSuite
=
"Default"
/>
32.
>
33.
>
34.
>
35.
>
36.
<
client
>
38.
binding
=
"basicHttpBinding"
bindingConfiguration
=
"CommonsSoap11"
39.
contract
=
"WebService.Commons"
name
=
"CommonsSoap11"
/>
The second step to call the C# assembly is to generate the managed C++ wrapper which will export a standard C function that could be called into the C++ toolkit plugin. This standard Windows dll will contain the following code:
1 - the export of the function in some .h file:
01.
// ReportingPartyC.h
02.
#pragma once
03.
04.
05.
#ifdef REPORTINGPARTY_EXPORTS
06.
#define REPORTINGPARTY_API __declspec(dllexport)
07.
#else
08.
#define REPORTINGPARTY_API __declspec(dllimport)
09.
#endif
10.
11.
extern
"C"
12.
{
13.
bool
REPORTINGPARTY_API GetUSI(
long
entityId,
char
* usi,
size_t
length);
14.
}
The implementation of the call to the C# assembly objects as follow:
01.
// This is the main DLL file.
02.
03.
#include "stdafx.h"
04.
#include "ReportingPartyC.h"
05.
#include "DotNetMacros.h"
06.
07.
08.
bool
REPORTINGPARTY_API GetUSI(
long
entityId,
char
* usi,
size_t
length)
09.
{
10.
if
(!usi || length<=0)
11.
return
false
;
12.
13.
System::String^ _usi = gcnew System::String(
""
);
14.
bool
_result = ReportingParty::WebServiceCall::GenerateUSI(entityId,_usi);
15.
if
(_result)
16.
{
17.
18.
char
* szUsi = STRING_TO_CHAR(_usi);
19.
strncpy_s(usi,length,szUsi,length-1);
20.
RELEASE_CHAR(szUsi);
21.
}
22.
return
_result;
23.
}
The macro STRING_TO_CHAR and RELEASE_CHAR will be detailed in some other post, since it ca be reused for other projects. It permits the conversion of C# unicode string to a basic C ANSI one. Once exported as above, the standard C function can be easily be called in any C executable. In order to activate the call to the webservice, the app.config file should be renamed using the syntax and should be placed in the current working directory.
A standard call to this function could be:
01.
// TestImplementation.cpp : Defines the entry point for the console application.
02.
//
03.
04.
#include "stdafx.h"
05.
#include "..\..\ReportingPartyC\ReportingPartyC.h"
06.
07.
08.
int
main(
int
argc,
char
* argv[])
09.
{
10.
char
_usi[256];
11.
bool
_ret = GetUSI(10001030,_usi,
sizeof
(_usi));
12.
return
0;
13.
}