I'm not getting any return response from the web service, input message XML is being received Ok.
Trying to get and return a custom XML conforming to an integration standard, that is why I'm using S.S.C.Message as a parameter.
using System.ServiceModel.Channels;
[ServiceContract(Namespace = "urn:hl7-org:v3")]
public interface IHL7v3
{
[OperationContract(Name = "PRPA_IN201301UV02", Action = "urn:hl7-org:v3:PRPA_IN201301UV02")]
Message PIXManager_PRPA_IN201301UV02(Message input);
}
public class PIXService : IHL7v3
{
public Message PIXManager_PRPA_IN201301UV02(Message input)
{
// this code is being reached and executing fine
return Message.CreateMessage(MessageVersion.Soap12, "Op", "Content"); // will be replaced by an actual XML text
}
}
Service setup code:
Uri baseAddress = new Uri("http://192.168.2.120:31002/HL7Service");
ServiceHost selfHost = new ServiceHost(typeof(PIXService), baseAddress);
selfHost.Description.Name = "PIXManager";
selfHost.Description.Namespace = "urn:hl7-org:v3";
try
{
System.ServiceModel.Channels.Binding binder = new WSHttpBinding(SecurityMode.None);
binder.Name = "PIXManager_Binding_Soap12";
selfHost.AddServiceEndpoint(
typeof(IHL7v3),
binder,
"PIX");
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
Console.WriteLine(smb.HttpGetUrl);
selfHost.Description.Behaviors.Add(smb);
selfHost.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex"
);
selfHost.Open();
ideas why?
Edit:
Added ReplyAction, still no reply
[OperationContract(Name = "PRPA_IN201301UV02", Action = "urn:hl7-org:v3:PRPA_IN201301UV02", ReplyAction = "urn:hl7-org:v3:MCCI_IN000002UV01")]
// ..
return Message.CreateMessage(MessageVersion.Soap12, "urn:hl7-org:v3:MCCI_IN000002UV01", "Something");
Change MessageVersion to MessageVersion.Soap12WSAddressing10
return Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "Op", "Content");
The ReplyAction of your operation is set to one value (I can't remember exactly which, but it's the default value), and you're creating the response message with an Action property of Op. Try setting the ReplyAction property in the OperationContract, and use the same value when creating the response message.
Related
I am new to .net and web services
It is possible to consume web services c#/.net with certificates and setting up security, also without adding a service reference?
If yes then how to do that
WCF or WSE?
How to add a security policy to it?
I have looked already to WCF, httpclient, X509store, certificates, WSE in official documents but didn't know to apply via code.
I need a response in string
Thank you
Adding a service reference is commonly used to generate a client proxy, while generating a client proxy can be done by using channel factory. Therefore, according to your requirement, we just create a WCF server-side with authentication the client with a certificate, this is enough.
And the client manage to call the service by ChannelFactory, Here is an example.
Server-side(console application)
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:21011");
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Message;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior()
{
HttpGetEnabled = true
};
sh.Description.Behaviors.Add(smb);
}
sh.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "5ba5022f527e32ac02548fc5afc558de1d314cb6");
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Opened += delegate
{
Console.WriteLine("Service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("Service is clsoed");
};
sh.Open();
Console.ReadLine();
//pause
sh.Close();
Console.ReadLine();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public class MyService : IService
{
public string Test()
{
return DateTime.Now.ToLongTimeString();
}
}
On the Client-side, we can consume the service by using channel factory.
class Program
{
static void Main(string[] args)
{
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Message;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
Uri uri = new Uri("http://vabqia969vm:21011");
ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
factory.Credentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.Root, X509FindType.FindByThumbprint, "5ba5022f527e32ac02548fc5afc558de1d314cb6");
factory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "f0969c5725b2f142b7f150515ec2bd12bc45250b");
var service = factory.CreateChannel();
var result = service.Test();
Console.WriteLine(result);
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
Please note that there are many ways to use certificate. Here the client-side and the server-side are protected by the certificate, and the sever-side authenticates the client with a certificate, client should provide a client certificate when calling the service.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-a-certificate-client
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication
Feel free to let me know if there is anything I can help with.
I want to use this WcfCoreMtomEncoder lib here in my .Net Core project but I'm not sure how to use it syntactically.
I have this code below but can't use MessageEncoding because I'm in a .Net Core project (no mtom support):
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport)
{
// MessageEncoding = WSMessageEncoding.Mtom, not supported in .Net Core
TransferMode = TransferMode.Streamed
};
EndpointAddress endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var documentAddResult = webService.DocumentAdd(document);
channelFactory.Close();
From what I read I can replace it with this library code below and I see from the documentation for the encoder lib that the usage looks like this:
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement());
var transport = new HttpTransportBindingElement();
var customBinding = new CustomBinding(encoding, transport);
var client = new MtomEnabledServiceClient(customBinding);
but I'm not sure what's what here?
How would it be used to perform the document upload I'm trying to achieve? And is the library doing this or am I misunderstanding what it does?
If anyone can provide me an example of how to use this library to perform the document upload it would be appreciated.
Proceed as follows:
Generate the service client using WCF, this will result in a namespace and partial class, say DocumentWebServiceClient,
in the same project and namespace, create a file to extend the partial class and implement the ConfigureEndpoint method which is intended for endpoint configuration tasks:
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using WcfCoreMtomEncoder;
public partial class DocumentWebServiceClient // DocumentWebServiceClient is generated by the WCF
{
static partial void ConfigureEndpoint(ServiceEndpoint serviceEndpoint, ClientCredentials clientCredentials)
{
var messageEncodingBindingElementType = typeof(MessageEncodingBindingElement);
var elements = serviceEndpoint.Binding.CreateBindingElements();
IEnumerable<BindingElement> elementsWithoutEncodingElement = elements.Where(item => !messageEncodingBindingElementType.IsAssignableFrom(item.GetType()));
var existingEncodingElement = (MessageEncodingBindingElement)elements.Where(item => messageEncodingBindingElementType.IsAssignableFrom(item.GetType())).First();
var newEncodingElement = new MtomMessageEncoderBindingElement(existingEncodingElement);
// Encoding is before transport, so we prepend the MTOM message encoding binding element
// https://learn.microsoft.com/en-us/dotnet/framework/wcf/extending/custom-bindings
var cb = new CustomBinding(elementsWithoutEncodingElement.Prepend(newEncodingElement));
serviceEndpoint.Binding = cb;
}
}
After some struggles, I managed to create a WCF service that could be consumed by the class library. But it only supports the Custombinding. Please refer to the below example.
Server-side (a console application based on Dotnet Framework 4.7.2)
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:21011");
MtomMessageEncodingBindingElement encoding = new MtomMessageEncodingBindingElement();
var transport = new HttpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
var binding = new CustomBinding(encoding, transport);
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior()
{
HttpGetEnabled = true
};
sh.Description.Behaviors.Add(smb);
}
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Opened += delegate
{
Console.WriteLine("Service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("Service is clsoed");
};
sh.Open();
Console.ReadLine();
//pause
sh.Close();
Console.ReadLine();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public class MyService : IService
{
public string Test()
{
return DateTime.Now.ToLongTimeString();
}
}
Client-side (Core-based Console application with WcfCoreMtomEncoder nuget package, calling the service by using ChannelFactory).
class Program
{
static void Main(string[] args)
{
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement());
var transport = new HttpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
var binding = new CustomBinding(encoding, transport);
EndpointAddress endpoint = new EndpointAddress("http://vabqia969vm:21011");
ChannelFactory<IService> channelFactory = new ChannelFactory<IService>(binding, endpoint);
var webService = channelFactory.CreateChannel();
Console.WriteLine(webService.Test());
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
One more thing we pay attention to is that we should manually bind a certificate to the particular port on the server-side if the server using Transport security mode to secure the communication.
Netsh http add sslcert ipport=0.0.0.0
certhash=0102030405060708090A0B0C0D0E0F1011121314
appid={00112233-4455-6677-8899-AABBCCDDEEFF}
In the above example, I bind a certificate that has a named vabqia969vm subject(DNS) to the machine(hostname is vabqia969vm). Here are some official links.
https://learn.microsoft.com/en-us/windows/win32/http/add-sslcert
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
On the client-side, before making a call to the service, we should establish a trust relationship so that communication is available between the client-side and the server-side. Therefore, I install the server certificate on the client-side LocalCA(Trusted Root Certification Authorities in the certification store). Alternatively, we could manually add a certificate validation process.
ChannelFactory<IService> channelFactory = new ChannelFactory<IService>(binding, endpoint);
channelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new System.ServiceModel.Security.X509ServiceCertificateAuthentication()
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck
};
Feel free to let me know if there is anything I can help with.
Updated.
I have updated the above example which works properly over HTTP protocol.
your .net core client code should be something like this
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement());
var transport = new HttpTransportBindingElement();
var customBinding = new CustomBinding(encoding, transport);
EndpointAddress endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(customBinding, endpoint);
var webService = channelFactory.CreateChannel();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var documentAddResult = webService.DocumentAdd(document);
channelFactory.Close();
Expanding on #divyang4481's answer. For those still struggling with this, depending on the service you are interacting with, you may have to change your encoding to something like the following:
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
});
var transport = new HttpsTransportBindingElement();
var customBinding = new CustomBinding(encoding, transport);
The web service I was calling threw an error, i.e. mustUnderstand headers with default TextMessageEncodingBindingElement, so, I had to set the AddressVersion to none to solve the issue.
I have a WCF service with a class that implements IContractBehavior and IWsdlExportExtension that generates a WSDL with a user's allowed operations and excludes operations and types they do not have access to.
The limitation of this is however is that for each user, I have to manually change which user I am generating the WSDL for.
I'd like to fix this limitation by passing in the user as part of the request for the metadata for example.
localhost/service.svc?user=me
or
localhost:9766/service.svc?singleWsdl&user=me
Alternatively I could use svcutil would also work as long as the resulting WSDL is flattened.
I was able to get this to work by doing the following
I host the service in console application.
I added an endpoint with the IContractBehavior added to it, passing in the desired parameter into the IContractBehavior class.
After the service is open, I use WsdlExporter to export the Metatadaset
Finally I use the WsdlHelper to generate the Wsdl file as
described here
http://www.shulerent.com/2013/03/14/generating-a-single-flattened-wsdl-from-an-existing-wcf-service/
The code
const string BASE_ADDRESS =
"http://localhost:8731/Design_Time_Addresses/CalcService";
var uri = new Uri(BASE_ADDRESS);
var user = "userName";
using (var serviceHost = new ServiceHost(typeof(Calc), uri))
{
var exporter = new WsdlExporter();
var endpoint = serviceHost.AddServiceEndpoint(typeof(ICalc),
new BasicHttpBinding(), "");
endpoint.Contract.Behaviors.Add(new
RestrictedOperationsWsdlExportExtensionAttribute(user));
serviceHost.Open();
Console.WriteLine("The service is ready: " + user);
exporter.ExportEndpoint(endpoint);
if (exporter.Errors.Count == 0)
{
var metadataSet = exporter.GetGeneratedMetadata();
var asy= Assembly.GetAssembly(typeof(WsdlExporter));
var t = asy.GetType("System.ServiceModel.Description.WsdlHelper",
true);
var method = t.GetMethod("GetSingleWsdl",
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.Static);
var serviceDescription =
method.Invoke(null, new object[] {metadataSet})
as System.Web.Services.Description.ServiceDescription;
if (serviceDescription != null)
{
serviceDescription.Name = "Calc";
serviceDescription.Write(user + ".wsdl");
}
}
}
I have a WCF service where everything is started programmatically (and need to continue doing so) and I want this service to respond to [WebGet] attributes.
However, when I call one of the WCF methods the service returns a 400 Bad Request.
This would initially seem like a duplicate of WCF Service Returns 400 error when using WebGet or Bad Request 400 while accessing WCF Rest service (WebGet) but both of these solutions add to web.config, a file I don't have because I need to do everything programmatically.
I have tried to add a WebHttpBinding to what seems like the endpoint, but it doesn't work properly (I'm probably not doing it the correct way).
The code below starts without errors, but when I try to go to http://localhost:8765/MyService/MyTest I get the aforementioned 400 Bad Request
What am I missing?
WCF starter
MyService myService = new MyService();
ServiceHost host = new ServiceHost(myService, new Uri("http://localhost:8765/MyService"));
ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
foreach(ServiceEndpoint endpoint in host.Description.Endpoints) {
endpoint.Binding = new WebHttpBinding();
}
host.Open();
Service interface
[ServiceContract]
public interface IMyService {
[OperationContract]
[WebGet]
string MyTest();
}
Service implementation
public class MyService : IMyService {
public string MyTest() {
return "Response from WCF service";
}
}
I use this code I wrote to initialize and start my WCF restful services completely from code:
public static WebServiceHost InitializeAndStartWebServiceHost(int port, string endPointName, object serviceModel, Type implementedContractType) {
var baseAddress = new Uri($"http://0.0.0.0:{port}/{endPointName}");
WebServiceHost host;
try {
host = new WebServiceHost(serviceModel, baseAddress);
} catch (Exception exception) {
Debug.Print("Error when creating WebServiceHost, message: " + exception.Message);
return null;
}
// ReSharper disable once UseObjectOrCollectionInitializer
var binding = new WebHttpBinding();
binding.UseDefaultWebProxy = false;
binding.BypassProxyOnLocal = true;
//By default, TransferMode is Buffered which causes C# wcf client to be slow as hell (>500ms for requests which give >2kB responses).
//I am not exactly sure why this helps, but it helps!
binding.TransferMode = TransferMode.Streamed;
host.AddServiceEndpoint(implementedContractType, binding, "");
var behavior = new WebHttpBehavior();
behavior.HelpEnabled = false;
behavior.DefaultBodyStyle = WebMessageBodyStyle.Bare;
// We will use json format for all our messages.
behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json;
behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
behavior.AutomaticFormatSelectionEnabled = false;
behavior.FaultExceptionEnabled = true;
host.Description.Endpoints[0].Behaviors.Add(behavior);
try {
host.Open();
} catch (AddressAccessDeniedException) {
Console.WriteLine(#"Application must run with administrator rights.");
Console.ReadKey();
Environment.Exit(0);
}
return host;
}
I am trying to create a WCF service at runtime. My service interface is:
[ServiceContract]
public interface IInformationService : IService
{
[OperationContract]
[WebInvoke(Method = "Get", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "Test", RequestFormat = WebMessageFormat.Json)]
string Test();
}
I am serving my service as follows:
var httpEnumerator = ImplementedContracts.Values.GetEnumerator();
httpEnumerator.MoveNext();
var httpContractType = httpEnumerator.Current.ContractType;
var webBinding = new WebHttpBinding()
{
Security =
{
Mode = WebHttpSecurityMode.None
}
};
var httpEndpoint = AddServiceEndpoint(
httpContractType,
webBinding, baseAddress+/Get"
);
httpEndpoint.Behaviors.Add(new CustomEndpointBehavior());
The ServiceHost is created by this method:
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var host = new WcfServiceHost(serviceType, baseAddresses);
if (host.Description.Behaviors.Contains(typeof(ServiceDebugBehavior)))
{
(host.Description.Behaviors[typeof(ServiceDebugBehavior)] as
ServiceDebugBehavior).IncludeExceptionDetailInFaults = true;
}
else
{
var debug = new ServiceDebugBehavior
{
IncludeExceptionDetailInFaults = true
};
host.Description.Behaviors.Add(debug);
}
if (host.Description.Behaviors.Contains(typeof(ServiceMetadataBehavior)))
{
(host.Description.Behaviors[typeof(ServiceMetadataBehavior)] as ServiceMetadataBehavior).HttpGetEnabled = true;
(host.Description.Behaviors[typeof(ServiceMetadataBehavior)] as ServiceMetadataBehavior).HttpsGetEnabled = true;
}
else
{
var smb = new ServiceMetadataBehavior
{
HttpGetEnabled = true,
HttpsGetEnabled = true
};
host.Description.Behaviors.Add(smb);
}
host.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpsBinding(),
"mex"
);
host.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex"
);
return host;
}
Service route creation:
var serviceRoute = new ServiceRoute(
"wcf.service/" + service.Value.Name,
new WcfServiceHostFactory(),
service.Value
);
if (!RouteTable.Routes.Contains(serviceRoute))
{
RouteTable.Routes.Add(serviceRoute);
}
When I try to access my service from a web browser using the address
http://localhost/Werp.View/wcf.service/InformationService/Get/Test
I obtain the following error:
<Fault xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
<Code>
<Value>Sender</Value>
<Subcode>
<Value xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">
a:ActionNotSupported
</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="en-US">
The message with Action '' cannot be processed at the receiver, due to a
ContractFilter mismatch at the EndpointDispatcher. This may be because of
either a contract mismatch (mismatched Actions between sender and receiver)
or a binding/security mismatch between the sender and the receiver. Check
that sender and receiver have the same contract and the same binding
(including security requirements, e.g. Message, Transport, None).
</Text>
</Reason>
Can anyone help me?
If you don't need specific WCF features or you have mandate to use WCF you should consider using different stack for REST based services. For example ASP.NET web API or ServiceStack. It looks like a lot of work to do a simple REST call.
If you turn on service diagnostics this might help diagnosing the problem. You can see this SO for detailed instructions
You can also refer to this SO: WCF - ContractFilter mismatch at the EndpointDispatcher exception for some ideas.
My problem has been solved when I added WebHttpBehavior to endpoint
httpEndpoint.Behaviors.Add(new WebHttpBehavior());
In my case, I needed to add a HTTP header to the request called SOAPAction, where the value was set to the xmlns path to the service I was requesting.