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.
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 would like to call a method from a web service when the requested URL is the root of the host.
[ServiceContract]
public interface ICalculator
{
[OperationContract]
[WebGet(UriTemplate = "/")]
string RootMethod();
[OperationContract]
[WebGet]
double Add(double x, double y);
}
Browsing to http://localhost/Add?x=1.1&y=2.2 Add() is executed and returns the result as expected, but when I browse to http://localhost/, RootMehtod() is not executed instead I get a message that tells me
Metadata publishing for this service is currently disabled.
How do I bind a method to the root of a self hosted WCF webservice?
First of all the error your are encountering is because you haven’t configured our service to expose any meta data about it. To expose a WSDL for a service we need to configure our service to provide meta information.
Now, you need to update your OperationContract like this :
[OperationContract]
[WebGet(UriTemplate = "")]
Note the difference in UriTemplate
After that you need to expose end-point like this :
string baseAddress = "http://" + Environment.MachineName;
ServiceHost host = new ServiceHost(typeof(TestService), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITestService), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
ServiceDebugBehavior debugBehavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
debugBehavior.HttpHelpPageEnabled = false;
debugBehavior.HttpsHelpPageEnabled = false;
host.Open();
Refer this link for more details , hope this helps !
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 use WCF (by means of using the WebChannelFactory) to invoke some services that are outside of my control, implemented in a variety of technologies. From the WCF perspective, my interface only has one method, let's call it "get-stuff". So, the same method can be implemented by these services as http://www.service-a.com/get-stuff, or as http://www.service-b.com/my-goodies/, or as http://www.service-c.com/retrieve-thing.php
In all examples I've seen the method binding to a particular URI is accomplished via the UriTemplate member of the WebGet/WebInvoke attribute. But this means, all the URIs for the "get-stuff" method must follow a fixed template. For, example, I can create a UriTemplate = "/get-stuff", so that my method will always be bound to /get-stuff.
However, I want my method to bind to any arbitrary URI. BTW, the parameters are passed as a POST data, so I do not need to worry about binding URI to parameters of the method.
why don't you do something like this
EndpointAddress endpointAddress = new EndpointAddress("any service url");
ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>(binding, endpointAddress);
IMyServiceclient = channelFactory.CreateChannel();
client.GetStuff();
OK, I have found a solution to the problem, by patching the UriTemplate of the WebInvokeAttribute at run-time. My single-method WCF interface is:
[ServiceContract]
interface IGetStuff
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
ResponseData GetStuff(RequestData request);
}
Here is how I get the handle to the interface:
//Find the last portion of the URI path
var afterLastPathSepPos = uri.LastIndexOf('/', uri.Length - 2) + 1;
var contractDesc = ContractDescription.GetContract(typeof(IGetStuff));
foreach (var b in contractDesc.Operations[0].Behaviors)
{
var webInvokeAttr = b as WebInvokeAttribute;
if (webInvokeAttr != null)
{
//Patch the URI template to use the last portion of the path
webInvokeAttr.UriTemplate = uri.Substring(afterLastPathSepPos, uri.Length - afterLastPathSepPos);
break;
}
}
var endPoint = new ServiceEndpoint(contractDesc, new WebHttpBinding(), new EndpointAddress(uri.Substring(0, afterLastPathSepPos)));
using (var wcf = new WebChannelFactory<I>(endPoint))
{
var intf = wcf.CreateChannel();
var result = intf.GetStuff(new RequestData(/*Fill the request data here*/)); //Voila!
}
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.