PowerShell and Webservice - Add header - wcf

I used the PowerShell New-WebServiceProxy commandlet to get a connection with a WebService(WCF/SOAP). It´s possible to connect to the WebService but when I want to execute a methode I´m getting a access denied. The access denied is because the WebService needs a custom message header. But this is not possible with New-WebServiceProxy.
Question: What is the easiest way to connect/use the WebService and add the message header? Is there a PowerShell example script?
(My prefered language is PowerShell in that case)
BTW: Yes I know that there is a Question very similar to my: Add custom SOAP header in PowerShell using New-WebServiceProxy
Thank you in advance!

This is more of a workaround, but maybe it works for you. Instead of using the cmdlet, create a C# oder VB.NET Project, add the WCF service reference as it is intended to be used. Then create a class that has a method for every service method you want to call and exposes the arguments you need to use in PowerShell.
class MyProxy
{
public string Url { get; set; }
public string SomeParameterForTheHeader { get; set; }
public string CallSomeMethod(string input1, string input2)
{
// create WCF context using this.Url
// create MessageHeader using this.SomeParameterForTheHeader and add it to the context
// call SomeMethod on the context using input1 and input2
}
}
Compile it and use the assembly and class in your PowerShell script.
[System.Reflection.Assembly]::LoadWithPartialName("MyAssembly") > $null
$ref = New-Object MyNamespace.MyProxy()
$ref.Url = "http://..."
$ref.SomeParameterForTheHeader = "your value here"
$ref.CallSomeMethod("input1", "input2")

Related

How to write a PHPUnit test for a SOAP server?

UPD. Sorry, guys.
I have an application that acts as a SOAP server, how do I write a PHPUnit test to test it?
SOAP extension is reading data from PHP input stream. You just provide your own data there and create some integration/unit tests for your API.
Take a look at the signature of SoapServer::handle() method. It takes as an argument a string which is a request itself. This parameter is optional and if you don't pass anything in, PHP will just read the data itself. But you can simply override it.
I used streams to do it. First you wrap the SoapServer with your own class like this:
class MyServer
{
/** \SoapServer */
private $soapServer;
public function __construct(\SoapServer $soapServer)
{
$this->soapServer = $soapServer;
}
public function handle(Psr\Http\Message\StreamInterface $inputStream): void
{
$this->soapServer->handle($inputStream->getContent());
}
}
Now you are ready to mock the request.
In your test you can do:
class MyTest extends TestCase
{
public function testMyRequest(): void
{
$mySoapServer = $this->createMySoapServer();
$request = $this->createRequest();
$mySoapServer->handle($request);
}
private function createRequest(): StreamInterface
{
$requestString = '<soap:Envelope></soap:Envelope>';
$fh = fopen('php://temp', 'rw');
fwrite($fh, $requestString);
fseek($fh, SEEK_SET);
return new Psr\Http\Message\StreamInterface\Stream($fh);
}
private function createMySoapServer(): MyServer
{
return new MyServer(new \SoapServer());
}
}
One thing to keep in mind - this test will generate output. You may want to test this output or ignore it. Depends on your use case.
Another side note. What you are asking for has really nothing to do with PHPUnit. It just a matter of designing your SOAP server correctly.
If you are wondering how to set up the stream when you have a live request, this is really simple:
$server->handle(new Psr\Http\Message\StreamInterface\Stream(fopen('php://input', 'r+')));

Retrieve WS request in CXF Web service

I got a CXF OSGi Web service (based on the example demo in servicemix: https://github.com/apache/servicemix/tree/master/examples/cxf/cxf-jaxws-blueprint)
The Web service works fine and i call all the available implemented methods of the service.
My question is how can i retrieve the request inside a WS method and parse in a string XML format.
I have found that this is possible inside interceptors for logging, but i want also to the WS-Request inside my methods.
For storing the request in the database I suggest to extend the new CXF message logging.
You can implement a custom LogEventSender that writes into the database.
I had similar requirement where I need to save data into DB once method is invoked. I had used ThreadLocal with LoggingInInterceptor and LoggingOutInterceptor. For example in LoggingInInterceptor I used to set the message into ThreadContext and in webservice method get the message using LoggingContext.getMessage() and in LoggingOutInterceptor I used to removed the message(NOTE: Need to be careful here you need to explictly remove the message from thread context else you will end up with memory leak, and also incase of client side code interceptors get reversed.
public class LoggingContext {
private static ThreadLocal<String> message;
public static Optional<String> getMessage() {
return Optional.ofNullable(message.get());
}
public static void setMessage(final String message) {
LoggingContext.message = new ThreadLocal<>();
LoggingContext.message.set(message);
}
}
Not an answer to this question but i achieved to do my task by using JAXB in the end and do some manipulations there.

Passing values in Header

We are consuming an external web service (WCF) in our AX2012 project. We followed the procedure described in the following blog.
We are implementing security by passing the token in the header. However, what i am not sure of is how to do this in AX2012.
the sample code for getting the token is
static void myTestServiceWSDL(Args _args)
{
myServiceWSDL.Proxies.Service.ServiceClient service;
myServiceWSDL.Proxies.Service.LoginData LoginData;
str token;
System.Exception ex;
System.Type type;
try
{
type = CLRInterop::getType('myServiceWSDL.Proxies.Service.ServiceClient');
service = AifUtil::createServiceClient(type);
LoginData = new myServiceWSDL.Proxies.Service.LoginData();
LoginData.set_uName("test");
LoginData.set_pwd("test");
token=service.Login(LoginData);
info(token);
}
catch(Exception::CLRError)
{
ex = CLRInterop::getLastException();
info(CLRInterop::getAnyTypeForObject(ex.ToString()));
}
}
The token comes back fine which confirms the code is working.
Now the question is how to do i set header values for the message.
If it was C# i would have done
using (MemberMasterClient proxy = new MemberMasterClient())
{
using (OperationContextScope scope
= new OperationContextScope(proxy.InnerChannel))
{
// set the message in header
MessageHeader header =
MessageHeader.CreateHeader("SourceApplication",
"urn:spike.WCFHeaderExample:v1",
"WCFClient Application 2");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
Console.WriteLine("Membership Details");
Console.WriteLine("Henry's - {0}", proxy.GetMembership("Henry"));
}
}
}
Could any one let me know how to do the equivalent in X++
One idea which has been on my mind is to write an assembly in C# which can then be called in AX2012. Will give that a go, but the idea is to code this in X++ in AX2012
The only thing you do differently in X++ is creating the proxy using the Aif utility. So basically, your C# example you listed, the only difference would be the proxy = new MemberMasterClient() which goes through AIF. All the other code you can take into X++ as-is (except for the "using"). You just need to have the right assemblies reference in the AOT, and use the full namespace in the code.
Alternatively, as you mentioned, you can just code it all in C# and call that from AX :-)

Powershell WCF service DateTime property always DateTime.Min (01.01.0001)

i've detected a strange behavior calling a WCF Service from a Powershell script. Using the command 'New-WebServiceProxy' from Powershell 2.0 get's you the abillity to send requests to a Webservice from a PS script. But i got some problems with System.DateTime objects on the service side, the value on the server side is always DateTime.Min.
So i created a small test service an script and i can reproduce this error. I used a 'standard' WCF-Project from VS2010 and extedended the 'DataContract' Class with a DateTime Property:
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
[DataMember]
public DateTime Datum { get; set; }
}
Powershell script to call the service:
cls
$serv = New-WebServiceProxy -uri 'http://localhost:50176/TestService.svc?wsdl' - Namespace wt
$data = [wt.CompositeType](New-Object wt.CompositeType)
$data.StringValue = "abcd"
$data.BoolValue = $true
$data.Datum = Get-Date
$serv.GetDataUsingDataContract($data)
If needed, i can send you a dropbox link for the zipped project.
Regards Uwe
I've never used powershell before but thought I'd take a long overdue look at it for this question!
The proxy object $data can have a date property set but, despite what your code looks like its doing, $data isn't the real object, just an XML proxy of it.
If you enter command "$data" you'll see what looks like an XmlSerialized version of the object (has xxSpecified properties for the bool and DateTime). It does reflect changes made by e.g. "$data.Datum = Get-Date".
The proxy is deserialised back to an instance of MyCompositeType when you call GetUsingDataContract (as its passed as a parameter and sent using XML) which you can see by putting breakpoints on the property get/setters prior to calling it.
As part of this deserialization, only the StringValue makes it which is because the Xml serialization for the other properties will only include values where "xxxSpecified" is true.
If you set the "xxxSpecified" properties in the proxy they will serialize back correctly.
But the best fix is to change their DataMember attribute to:
[DataMember(IsRequired=true)]
Which should just work with the code you've got.

How do you set the Customvalidation in Metadata file, If the Metadata is in different Model project

My silverlight solution has 3 project files
Silverlight part(Client)
Web part(Server)
Entity model(I maintained the edmx along with Metadata in a seperate project)
Metadata file is a partial class with relavent dataannotation validations.
[MetadataTypeAttribute(typeof(User.UserMetadata))]
public partial class User
{
[CustomValidation(typeof(UsernameValidator), "IsUsernameAvailable")]
public string UserName { get; set; }
}
Now my question is where I need to keep this class UsernameValidator
If my Metadata class and edmx are on Server side(Web) then I know I need to create a .shared.cs class in my web project, then add the proper static method.
My IsUserAvailable method intern will call a domainservice method as part of asyc validation.
[Invoke]
public bool IsUsernameAvailable(string username)
{
return !Membership.FindUsersByName(username).Cast<MembershipUser>().Any();
}
If my metadata class is in the same project as my domain service is in then I can call domain service method from my UsernameValidator.Shared.cs class.
But here my entity models and Metadata are in seperate library.
Any idea will be appreciated
Jeff wonderfully explained the asyc validation here
http://jeffhandley.com/archive/2010/05/26/asyncvalidation-again.aspx
but that will work only when your model, metadata and Shared class, all are on server side.
There is a kind of hack to do this. It is not a clean way to do it it, but this is how it would probably work.
Because the .shared takes care of the code generation it doesn't complain about certain compile errors in the #if brackets of the code. So what you can do is create a Validator.Shared.cs in any project and just make sure it generates to the silverlight side.
Add the following code. and dont forget the namespaces.
#if SILVERLIGHT
using WebProject.Web.Services;
using System.ServiceModel.DomainServices.Client;
#endif
#if SILVERLIGHT
UserContext context = new UserContext();
InvokeOperation<bool> availability = context.DoesUserExist(username);
//code ommited. use what logic you want, maybe Jeffs post.
#endif
The compiler will ignore this code part because it does not meet the condition of the if statement. Meanwhile on the silverlight client side it tries to recompile the shared validator where it DOES meet the condition of the if-statement.
Like I said. This is NOT a clean way to do this. And you might have trouble with missing namespaces. You need to resolve them in the non-generated Validator.shared.cs to finally let it work in silverlight. If you do this right you can have the validation in silverlight with invoke operations. But not in your project with models and metadata like you would have with Jeff's post.
Edit: I found a cleaner and better way
you can create a partial class on the silverlight client side and doing the following
public partial class User
{
partial void OnUserNameChanging(string value)
{
//must be new to check for this validation rule
if(EntityState == EntityState.New)
{
var ctx = new UserContext();
ctx.IsValidUserName(value).Completed += (s, args) =>
{
InvokeOperation invop = (InvokeOperation) s;
bool isValid = (bool) invop.Value;
if(!isValid)
{
ValidationResult error = new ValidationResult(
"Username already exists",
new string[] {"UserName"});
ValidationErrors.Add(error;
}
};
}
}
}
This is a method generated by WCF RIA Services and can be easily partialled and you can add out-of-band validation like this. This is a much cleaner way to do this, but still this validation now only exists in the silverlight client side.
Hope this helps