I've researched this for a day now and am turning to you experts here for your advice.
I have REST service that is the main IIS hosted service.
This service needs to interface with a 32-bit unmanaged .dll. In order to do this I'm taking one common approach which is creating a self hosted service and making calls to it.
With that background, I'm having one heck of a time getting the self hosted one working propertly.
here is the code that WORKS:
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/theSvc");
Uri mexUri = new Uri("http://localhost:8080/theSvc/mex");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(ImgService), baseAddress))
{
// var wsHttpBinding = new WSHttpBinding();
// wsHttpBinding.MaxReceivedMessageSize = int.MaxValue;
// host.AddServiceEndpoint(typeof(IImgService), wsHttpBinding, baseAddress);
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetUrl = mexUri;
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 <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
Now...if I uncomment these lines in order to increase the maxreceivedMessageSize and remove the baseAddress from the using statement, I can no longer add a reference to the service:
So change this:
using (ServiceHost host = new ServiceHost(typeof(ImgService), baseAddress))
To this:
using (ServiceHost host = new ServiceHost(typeof(ImgService)))
And uncomment this:
// var wsHttpBinding = new WSHttpBinding();
// wsHttpBinding.MaxReceivedMessageSize = int.MaxValue;
// host.AddServiceEndpoint(typeof(IImgService), wsHttpBinding, baseAddress);
The error that I receive is:
... -> local host
There was an error downloading 'http://...:8080/theSvc/_vti_bin/ListData.svc/$metadata'.
The request failed with HTTP status 400: Bad Request.
Metadata contains a reference that cannot be resolved: 'http://...:8080/theSvc'.
Metadata contains a reference that cannot be resolved: 'http://...:8080/theSvc'.
If the service is defined in the current solution, try building the solution and adding the service reference again.
As like everyone else that posts here...I'm in a bind. Any help really appreciated.
It would appear you are not adding the service endpoint.
This is a function I'm using to startup an http host, however I am hosting it in a windows service. Which is the same as your console application. But IIS might think about it a bit differently.
static Uri scannerSvcBaseAddress;
static BasicHttpBinding scannerBinding;
static ServiceHost scannerHost;
public void startScannerWcfService(long eventID)
{
try
{
db.writeServiceLog(eventID, "STARTUP", "=== Scanner WCF Service Starting ===");
string addressToHostAt = ConfigurationManager.AppSettings["ScannerHostBaseAddress"];
if (addressToHostAt != null && addressToHostAt.Trim() != string.Empty)
{
scannerSvcBaseAddress = new Uri(addressToHostAt);
scannerHost = new ServiceHost(typeof(BarCodeService.ScannerService), scannerSvcBaseAddress);
//Allows publishing of METADATA to developers when self hosted on a remote system
ServiceMetadataBehavior smb = scannerHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior();
}
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
scannerHost.Description.Behaviors.Add(smb);
scannerHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
scannerBinding = new BasicHttpBinding();
scannerBinding.MaxReceivedMessageSize = 2147483647;
scannerBinding.Security.Mode = BasicHttpSecurityMode.None;
scannerBinding.OpenTimeout = new TimeSpan(0, 0, 10);
scannerBinding.CloseTimeout = new TimeSpan(0, 0, 10);
scannerBinding.SendTimeout = new TimeSpan(0, 5, 0);
scannerBinding.ReceiveTimeout = new TimeSpan(0, Global.scanUserIdleLogout * 2, 0);
//scannerBinding.ReliableSession.InactivityTimeout = new TimeSpan(2, 0, 0, 0);
scannerHost.AddServiceEndpoint(typeof(BarCodeService.IScannerService), scannerBinding, "");
var behavior = scannerHost.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
scannerHost.Open();
db.writeServiceLog(eventID, "STARTUP", "=== Scanner WCF Service Started ===");
}
else
throw new Exception("Host Base Address not provided");
}
catch (Exception ex)
{
db.writeServiceLog(eventID, "STARTUP", string.Format("Error in ServiceManager.startScannerWcfService: {0} {1}", ex.Message, ex.StackTrace));
throw;
}
}
All that said, as was mentioned above if you only need to expose a string WCF may be overkill. I'm using WCF in a windows services to connect to handheld scanners in a warehouse environment, and I am really happy with how it performs. Though it was a huge pain to get it working initially.
But since the question was asked in the context of WCF I thought I would respond with a known to work function that does essentially what you want to do.
Related
No idea what's going on. Simple application hosted service. Ran fine on server A. Copied everything over to server B...and suddenly won't launch.
Any tips? Ideas? I'll happily provide more info. Thanks for any help.
Error message:
The communication object, System.ServiceModel.ServiceHost, cannot be
used for communication because it is in the Faulted state.
Code (FAILS AT HOST.OPEN()_
static void Main(string[] args)
{
try
{
Uri baseAddress = new Uri("http://localhost:8080/Brp");
Uri mexUri = new Uri("http://localhost:8080/Brp/mex");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(BBService), baseAddress))
{
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetUrl = mexUri;
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
BasicHttpBinding binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Mode = BasicHttpSecurityMode.None;
host.AddServiceEndpoint(typeof(IBService), binding, "");
// Enable metadata publishing.
var behavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
host.Open();
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
} catch (Exception excep)
{
writeMessage("EXCEPTION!!! - " + excep.Message);
}
In case anyone else runs into this do: Right-click -> Run as administrator
You have to follow certain rules while working with Message contract
1. When using Message contract type as parameter, Only one parameter can be used in servicie Operation
[OperationContract]
void SaveEmployeeDetails(EmployeeDetails emp);
2. Service operation either should return Messagecontract type or it should not return any value
[OperationContract]
EmployeeDetails GetEmployeeDetails();
3. Service operation will accept and return only message contract type. Other data types are not allowed.
[OperationContract]
EmployeeDetails ModifyEmployeeDetails(EmployeeDetails emp);
Note: If a type has both Message and Data contract, service operation will accept only message contract.
I have my WCF in my service References and when I run my WCF at the same time as I run my WPF using visual studio run multi projects then it all works fine, however I am now connecting multiple clients and if they start at the same time before no data is entered then it works. If one starts enters data then the other starts then the entered data is wiped. I have tried having it so it will start by running a host from my WPF. Unfortunately I get an error saying httpGetEnabled needs to be false, if this is false then I cannot update my service reference as it says there is an access issue. The code I have used for trying to run the host is.
try
{
ServiceHost host;
Service1.Service1Client service = new Service1.Service1Client();
string baseAddress = "http://localhost:59849/Service1.svc";
host = new ServiceHost(typeof(Service1.Service1Client));
host.AddServiceEndpoint(typeof(Service1.IService1),new BasicHttpBinding(), baseAddress);
host.Open();
wcfHostId = wcf.generateId();
textBox5.Text = "" + wcfHostId;
button5.IsEnabled = false;
}
catch (Exception ex)
{
MessageBox.Show("Error = " + ex.Message);
}
Edit
So basically what I was saying is when I was self hosting a WPF and a new client connected it was wiping all the variables stored inside the service. And I was enquiring was it because of the way I was hosting the service?
The error was the way I was trying to discover the WCF and didn't add a discovery endpoint.
host.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
host.AddServiceEndpoint(new UdpDiscoveryEndpoint());
needed to be added to the host part and the client code in the end was
var ep = "http://" + System.Net.Dns.GetHostName() + ":8732/DatabaseTransfer/Service1/";
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.None;
binding.SendTimeout = new System.TimeSpan(0, 1, 30);
ChannelFactory<IService1> wcfFactory = new ChannelFactory<IService1>(binding, new EndpointAddress(ep));
IService1 wcf = wcfFactory.CreateChannel();
I need to develop a WCF Hosted in a console app WebService.
I made it work using the Mutual Certificate (service and client) method using SecurityMode.Message.
But now i need to change the Security Mode to SecurityMode.Transport and use the wsHttpBinding with SSL. I made this code to host the service but i cannot get the wsdl with the browser, or execute some webmethod in the console app client.
static void Main()
{
var httpsUri = new Uri("https://localhost:8089/HelloServer");
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var host = new ServiceHost(typeof(WcfFederationServer.HelloWorld), httpsUri);
host.AddServiceEndpoint(typeof(WcfFederationServer.IHelloWorld), binding, "", httpsUri);
var mex = new ServiceMetadataBehavior();
mex.HttpsGetEnabled = true;
host.Description.Behaviors.Add(mex);
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The service is up, but i cannot get nothing on the https://localhost:8089/HelloServer.
On fiddler the get request via browser shows me this message:
fiddler.network.https> HTTPS handshake to localhost failed. System.IO.IOException
What im missing here?
Thanks
EDIT:
The Console Application Client Code
static void Main()
{
try
{
var client = new HelloWorldHttps.HelloWorldClient();
client.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPeople,
X509FindType.FindBySubjectName,
"www.client.com");
Console.WriteLine(client.GetData());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
Getting this error:
Could not establish trust relationship for the SSL/TLS secure channel
When it comes to the service, you need to map the certificate to the specific port as described here
http://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx
As for the client, you need to skip the verification of certificate properties like valid date, the domain by relaxing the certificate acceptance policy. An easiest way would be to accept any certiticate
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true
You can finetune the acceptance callback according to the docs to best fit your needs.
Ok so I am hosting a WCF service within a console application.
all bindings are created programatically, so no config settings.
I have a working service as long as I use HttpTransportBindingElement however as soon as I use HttpsTransportBindingElement then nothing works, the service does not display within the browser and the client application returns a 405 (Method Not Allowed) CommunicationException
I have tried setting a SecurityBindingElement to my CustomBinding but I am not sure which option I should be using.
SecurityBindingElement.CreateCertificateOverTransportBindingElement()
SecurityBindingElement.CreateAnonymousForCertificateBindingElement()
etc.
The code for the creation of the host is below
baseAddress = new Uri(string.Format("{0}://{1}", strConnectionType, ConfigurationManager.AppSettings["baseAddress"]));
ServiceHost host = new ServiceHost(typeof(IMyService), baseAddress);
host.AddServiceEndpoint(typeof(MyService), binding, String.Empty);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpsGetEnabled = certificate != null;
smb.HttpGetEnabled = certificate == null;
host.Description.Behaviors.Add(smb);
ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>();
if (sdb == null)
{
host.Description.Behaviors.Add(new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
}
else
{
if (!sdb.IncludeExceptionDetailInFaults)
{
sdb.IncludeExceptionDetailInFaults = true;
}
}
if (certificate != null)
{
host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, certificate.Thumbprint);
}
I followed this blog http://blogs.msdn.com/b/james_osbornes_blog/archive/2010/12/10/selfhosting-a-wcf-service-over-https.aspx which highlighted that in order for HTTPS to work you need to bind the port to the certificate you are using.
Process bindPortToCertificate = new Process();
bindPortToCertificate.StartInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "netsh.exe");
bindPortToCertificate.StartInfo.Arguments = string.Format("http add sslcert ipport=0.0.0.0:{0} certhash={1} appid={{{2}}}", port, certificate.Thumbprint, Guid.NewGuid());
bindPortToCertificate.Start();
bindPortToCertificate.WaitForExit();
once this was done it all worked.
contact me if any requires my example code of setting up and configuring a self-hosted WCF server with bindings programatically set. :)
I have a hosted WCF service in my console application as follow:
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/Test");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(TestService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("The Test service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
I have a client in a Windows Store (WinRT) application. I'm getting
"(413) Request Entity Too Large"
when trying to pass a large byte array. How can I set MaxReceivedMessageSize in my service by code?
You need to create a Binding, and then specify MaxReceivedMessageSize:
Uri baseAddress = new Uri("http://localhost:8080/Test");
var serviceHost = new ServiceHost(typeof(TestService));
var basicHttpBinding = new BasicHttpBinding();
basicHttpBinding.MaxReceivedMessageSize = int.MaxValue;
serviceHost.AddServiceEndpoint(typeof(IService), basicHttpBinding, baseAddress);
If your byte array is too big, then you could always split it to smaller blocks and send those in a loop. You might even want do it in another thread and update progress to the user interface.