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;
}
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 am new to programming and i have given a project which i have no idea how will to it.
I have to write a host application.The following is the requirement:
Develop CXML webservice host for testing CXML posts from xyz.net .
The application should read the stream of data,validate against the dtd. store it in to the corresponding tables and send a response back to the client.
This should help you for example. create a console application, and set the startup object to SelfHost. Run the application and you wcf service should be available at the URL stated into the code : http://localhost:1234/hello"
[ServiceContract()]
public interface IIndexer
{
[OperationContract]
bool Test();
}
public class Indexer : IIndexer
{
public bool Test()
{
return true;
}
}
class SelfHost
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:1234/hello");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(Indexer), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press Q and <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
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.
I create my host with the endpointaddress it needs to use, like this:
Uri endpointAddress = new Uri("http://127.0.0.1:5555/MyService");
ServiceHost host = new ServiceHost(myServiceType, endpointAddress);
host.AddServiceEndpoint(implementedContract, basicHttpBinding, string.Empty);
but when I later do host.Open(); I get the exception "HTTP could not register URL http://+:80/MyService/ because TCP port 80 is being used by another application". The result is the same when I do
host.AddServiceEndpoint(implementedContract, basicHttpBinding, endpointAddress);
Why would it try to do anything with port 80? How can I solve this?
Update
While trying to provide a more complete code sample, I found the culprit. I added a ServiceMetadataBehavior as follows:
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.ExternalMetadataLocation = new Uri(this.ExternalMetadataLocation);
smb.HttpGetEnabled = true;
smb.HttpGetUrl = this.RemovePort(this.EndpointAddress);
host.Description.Behaviors.Add(smb());
It seems that in the past I needed to remove the port from the URL to get this to work, but now that's exactly what causes the problem.
So Pratik is right: metadata were the problem, and of course the problem with port 80 is that there's some newly installed application using that port.
Thanks, regards,
Miel.
This is because port 80 is already used by another applicatiion as the error message says, most likely by local IIS server. Try stopping IIS and see, or use another port.
On vista and above you can use netsh command to check which ports are already reserved
BTW do you have http metadata or mex endpoints in the app.config or web.config file ?
The following works fine for me:
class Program
{
[ServiceContract]
public interface IMyService
{
[OperationContract]
void Test();
}
public class MyService : IMyService
{
public void Test()
{
throw new NotImplementedException();
}
}
static void Main()
{
var endpointAddress = new Uri("http://127.0.0.1:5555/MyService");
using (var host = new ServiceHost(typeof(MyService), endpointAddress))
{
var basicHttpBinding = new BasicHttpBinding();
host.AddServiceEndpoint(typeof(IMyService), basicHttpBinding, string.Empty);
host.Open();
}
}
}
Maybe you have some other code which is interfering?
I've written a WCF duplex service and client. Everything works well until I try to call .Demand() in the client implementation. It appears that the the service invokes the callback method Anonymously. I think I am missing how to correctly configure the service.
Code used to create ServiceHost;
ServiceHost duplex = new ServiceHost(new ServerWCallbackImpl());
NetTcpBinding secureBinding = new NetTcpBinding(SecurityMode.Message);
secureBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
duplex.AddServiceEndpoint(typeof(IServerWithCallback),
secureBinding,
"net.tcp://localhost:9080/DataService");
Console.WriteLine(Thread.CurrentPrincipal.Identity.Name); //<-- this correctly shows the current principal
duplex.Open();
if (duplex.State == CommunicationState.Opened)
((ServerWCallbackImpl)duplex.SingletonInstance).Send("Hello World!");
Code used to create client;
CallbackImpl callbackInstance = new CallbackImpl();
NetTcpBinding secureBinding = new NetTcpBinding(SecurityMode.Message);
secureBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
DuplexChannelFactory<IServerWithCallback> cf = new DuplexChannelFactory<IServerWithCallback>(
callbackInstance,
secureBinding,
new EndpointAddress(requestingEndpointAddress));
cf.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
cf.Credentials.Windows.ClientCredential = (NetworkCredential)CredentialCache.DefaultCredentials;
IServerWithCallback srv = cf.CreateChannel(new InstanceContext(callbackInstance));
srv.InitiateConversation();
Client implementation:
public void MethodOnClient(string message)
{
Console.WriteLine(Thread.CurrentPrincipal.Identity.Name); // <-- anonymous
PrincipalPermission p = new PrincipalPermission(#"DOMAIN\User", null);
p.Demand(); // <-- fails
}
How can I configure so that the ServiceHost correctly invokes the Callback with Windows credentials?
Does setting TokenImpersonationLevel to Delegation instead of Impersonation? Like this:
cf.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
See this MSDN article.