I am running into a very strange issue here folks. For simplicity I created a project for the sole purpose of testing the issue outside the framework of a larger application and still encountered what is either a bug in WCF within Visual Studio 2010 or something related to my WCF newbie skill set : )
Here is the issue:
I have a WCF endpoint I created running inside of an MVC3 project called "SimpleMethod". The method runs inside of a .svc file on the root of the application and it returns a bool.
Using the "WCF Service Configuration Editor" I have added the endpoint to my Web.Config along with a called "LargeImageBinding".
Here is the service:
[OperationContract]
public bool SimpleMethod()
{
return true;
}
And the Web.Config generated by the Config Tool:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="LargeImageBinding" closeTimeout="00:10:00" />
</wsHttpBinding>
</bindings>
<services>
<service name="WCFEndpoints.ServiceTestOne">
<endpoint address="/ServiceTestOne.svc" binding="wsHttpBinding"
bindingConfiguration="LargeImageBinding" contract="WCFEndpoints.IServiceTestOne" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
The service renders fine and you can see the endpoint when you navigate to: http://localhost:57364/ServiceTestOne.svc - Now the issue occurs when I create a separate project to consume the service. I add a service reference to a running instance of the above project, point it to: http://localhost:57364/ServiceTestOne.svc
Here is the weird part. The service automatically generates just fine but In the Web.Config the endpoint that is generated looks like this:
<client>
<endpoint address="http://localhost:57364/ServiceTestOne.svc/ServiceTestOne.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceTestOne"
contract="ServiceTestOne.IServiceTestOne" name="WSHttpBinding_IServiceTestOne">
As you can see it lists the "ServiceTestOne.svc" portion of the address twice!
When I make a call to the the service I get the following error:
The remote server returned an error: (404) Not Found.
I tried removing the extra "/ServiceTestOne.svc" at the end of the endpoint address in the above config, and I get the same exact error.
Now what DOES work is if I go back to the WCF application and remove the custom endpoint and binding references in the Web.Config (everything in the "services" and "bindings" tags) then go back to the consumer application, update the reference to the service and make the call to SimpleMethod()....BOOM works like a charm and I get back a bool set to true.
The thing is, I need to make custom binding configurations in order to allow for access to the service outside of the defaults, and from what I can tell, any attempt to create custom bindings makes the endpoints seem to run fine, but fail when an actual method call is made.
Can anyone see any flaw in how I am putting this together? Thank you for your time - I have been running in circles with this for about a week!
When hosting a WCF service in IIS, the base address of the service is formed using the following format: {protocol}://{host}:{port}/{applicationName}/{svcFileName}. This is the address you can browse to get the WCF help page and/or the metadata (on a default configuration).
To form the actual address of the endpoint (the one your client needs to use), the following format is used: {serviceBaseAddress}/{endpointAddress}
Comparing the format above and the sample configuration you provided explains why you're getting ServiceTestOne.svc twice in your client address.
If you want the address of your service to be http://localhost:57364/ServiceTestOne.svc, I'd recommend leaving the address attribute empty in your endpoint configuration:
<endpoint address="" ... />
Related
This is going to be a weird question, but:
I'm currently running a wcf service on my website (made in Visual Studio 2012 - .scv file). I have a console client which I try to test the connection with in addition to the built in wcf test client. The test client runs fine and I'm able to call upon the functions, but when I stop running the site/test client (which should mean host by extension, right?), my console client can still run and talk to the service just fine.
I'm also able to visit the WCF service page while it's not running in the browser. When I created the WCF in console, I was not able to do any of these things while the host wasn't actually running. Because of this I'm wondering if there's something wrong with the code that I'm just not seeing.
I could see this developing into an issue when I try to get the website online on a server (since my testing client won't be located on the same machine as the service. I'm assuming this is happening because I have access to the files even when it's not running).
What should I do/what is wrong?
Thank you for your time.
relevant web.config code:
<system.serviceModel>
<services>
<!-- ICanHasGamez=solution that holds the webpage&wcfservice -->
<service name="ICanHasGamez.APIHost" behaviorConfiguration="behavior">
<endpoint address="" binding="basicHttpBinding" contract="ICanHasGamez.IAPIHost">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name ="behavior">
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
app.config code from the Console Client:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IAPIHost" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:2105/APIHost.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IAPIHost" contract="ServiceReference1.IAPIHost"
name="BasicHttpBinding_IAPIHost" />
</client>
</system.serviceModel>
</configuration>
In your website project's properties under the web tab, are you hosting it in IIS? If so, the WCF service will keep running, even if you're not using the built in WCF test client. IIS runs on its own, outside of the visual studio environment. You can turn off your service by stopping it in IIS manager, if you'd like, but otherwise it's usable outside of VS.
In contrast, running the WCF service in a console (known as self-hosted) will close down when it's host application, i.e your console application, closes.
In short, if you are using local IIS for your web project, then this isn't anything you've done wrong and is expected behaviour. And regarding running your service on a different machine to your test client, that's not a problem. If you're running it in IIS, then your hosting computer just needs IIS turned on and that project running in IIS there. Voila! If you're running the service as self-hosted, i.e in a console, just leave the console open. Check out this StackOverflow question for some of the pros and cons of self-hosted wcf vs IIS (the accepted answer is a bit local to that particular OP, but the answers below are more informative).
I hope this helps and I hope I'm not barking up the wrong tree here.
I don't see anything wrong with the scenario you described - its expected behavior.
When you host the WCF service in IIS, as long as IIS is up and running (and there's no other problems), your service can receive requests. If the service host has been disposed (due to inactivity timeout or other reasons), if a new request is received IIS will spin up a new instance.
On the other hand, if you're self-hosting the WCF service in, for example, a console app, then the only time that service is able to respond to requests is when the self-hosting application is running.
So to answer your question, you're not doing anything wrong, and you don't need to do anything different. You should be able to simply deploy the WCF Service to the remote server, and then access it with your client.
We need to support both SOAP as well as REST interfaces into what is effectively the same function call. I understand SOAP is a protocol while REST is an architecture style, but I'm also sure web service developers know what I'm talking about; so lets not get distracted by the pedantic details. Also, I'd request avoiding a SOAP versus REST debate - we need both for business needs across different customers. The platform is ASP.NET 4.0.
REST:
I'm using WCF and essentially inputData is consumed by FunctionA to produce OutputData. I've got the abstract interface defined via the [ServiceContract] and the actual service logic exposed via the [WebInvoke] keyword. i.e.
[WebInvoke(UriTemplate = "funcA", Method = "POST", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
public OutputType FunctionA(InputType inputData)
SOAP
For the SOAP interface, I essentially need the same inputData going into the same FunctionA() which spits out the same OutputData (of type OutputType). Of course, these will be wrapped inside SOAP envelopes etc.
So my question is: How can I leverage the framework so that supporting a SOAP interface into the same functionality can be done with minimal custom code?
Find my service below that provides support for both SOAP and REST
[ServiceContract]
public interface ISampleService
{
[WebGet]
[OperationContract]
string GetData();
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SampleService : ISampleService
{
public string GetData()
{
return "Welcome to server";
}
}
My web.config would have the following
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="RestBinding">
<readerQuotas maxStringContentLength="5242880" maxArrayLength="16384"
maxBytesPerRead="4096" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="XMLService.SampleService">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding"
bindingConfiguration="RestBinding" name="SampleService" contract="XMLService.ISampleService" />
<endpoint address="soap" binding="basicHttpBinding" contract="XMLService.ISampleService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Now the URL for your service would look like as follows:
REST --> http://localhost/SampleApp/SampleService.svc/GetData
SOAP --> http://localhost/SampleApp/SampleService.svc
If you want the REST url to be clean i.e. not have .svc in it then you can follow the WebAPI approach where you register your route in Global.asax and removed the endpoint from the config. You can perform tha same for your POST operation. Also make sure that if you are passing in complex types you need to let the service know if the types as shown below
[KnownType(typeof(SampleItem))]
The above attribute needs to be placed on the class implementing the interface.
One of the projects I'm working on has this exact functionality.
Our WCF service is multi tired
Data Access Layer
Business Logic Layer
Service Layer
The Service Layer has 2 End Points
SOAP
Rest
The End Points are very thin and basically consume methods from the BLL. So taking your example, Function A would be defined in the Business Logic Layer, where all the work would be done. Each of your Endpoints Would also have FunctionA which calls FunctionA from the BLL.
The Visual Studio Solution for this consists of a project for each of the BLL and DAL layers and a project for each of the end points. There are also a couple of other projects for unit tests etc
You have two good options:
Create a SOAP Service and a REST Service. The Endpoint of each calsl a shared class (so there is no code duplication, eg:
//SOAP endpoint
[webmethod]
public void DoSomething(int id)
{
myClass.MethodThatActuallyDoesTheWork(id);
}
//in another service we have the REST methods
//REST endpoint
public void DoSomething(int id)
{
myClass.MethodThatActuallyDoesTheWork(id);
}
OR (my prefered method)
Install the WCFWebAPI via Nuget (or whatever) as this allows content negotioation in that you write one method in your service. If THE CLIENT specifies Accept: application/xml then they get XML returned, if the client specifies Accept: application/json then they get JSON back.
I am not sure if this generates a WSDL though - if your using soap 'cos you want XML then this is a good method. If you need a WSDL then two services might be better.
Food for thought anyway. Hope this helps.
If you need to test a WSDL based XML service the WCFTestClient is pretty handy.
The WCFWebApi actually generates test/help pages for you too. It's great.
We have deployed a web service on a production server and still getting this error once trying to get a call to it :
"There was no endpoint listening at http://www.ourproductionserver.com/ClientService.svc/mex that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details."
When looking into the InnerException, it return us a 404 File Not Found...
Weird part, is that if we copy/past the exact same url as giving in the error below (http://www.ourproductionserver.com/ClientService.svc) in IE or FireFox, it shows correctly. Heum, what do we do wrong?
We've also tried it on a local server and everything works fine.
Anyone alrealy get something similar and how to resolve it?
Note : Sorry for my english, I usualy talk french.
Our webconfig :
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
<webHttpBinding>
<binding name="WebBinding"></binding>
</webHttpBinding>
</bindings>
<services>
<service name="ourLib.ServiceImplementations.ClientActionService" behaviorConfiguration="ServiceBehavior">
<endpoint name="WebEndpoint" behaviorConfiguration="EndpointBehavior" address="" binding="webHttpBinding"
contract="ourLib.ServiceContracts.IClientActionService">
</endpoint>
<endpoint contract="ourLib.ServiceContracts.IClientActionService" address="mex" binding="mexHttpBinding"></endpoint>
</service>
<system.serviceModel>
Our ClientService.svc
<%# ServiceHost Language="C#" Debug="true" Service="ourLib.ServiceImplementations.ClientActionService" %>
UPDATE
We discover that the ISP is set to medium trust level when we are set to Full trust. Can it change something? If yes, how could we change it other than web.config (which we alrealy tried).
What is "/mex"? Is that a function within your service class? I just tested on my own Webservice.svc and wasn't able to do / so I'm not sure you can actually do that...
If you are trying to get a client to connect to your service, you should be able to just use the .svc URL and then invoke a function on the .NET-generated client object.
It looks like you're trying to make a call out to the metadata exchange endpoint (mex) which is only used when generating the metadata (the WSDL) in the first place. I would suggest checking where you set the service url and making sure you don't have the mex part on the end.
Well neither of us or the ISP find the reason why so instead of talking to the webservice to gain access to the DB, they've put a public IP Address so we can access to the Database directly via this IP instead of goin through a webservice.
Thanks you all but I would love to find out why in case this happen another time in the futur ...
I am working on a WCF application that will be deployed to various servers along the way and I would like to not have to remember to change the app.config every time I do a deployment. At first, my app.config serviceModel section looked like this:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
<behaviors>
<serviceBehaviors>
<behavior name="MyDefaultServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8888/MyService" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyDefaultServiceBehavior" name="MyService">
<endpoint address="net.tcp://localhost:9001/MyService" binding="netTcpBinding" contract="IMyService" name="NetTcpBinding_IMyService" />
</service>
</services>
This works fine in development when I was accessing the service running on my local machine. When I deployed it, the WSDL contained absolute paths that still pointed to localhost:
<xsd:import schemaLocation=http://localhost:8888/MyService?xsd=xsd0 namespace="http://tempuri.org/" />
So, I can change the httpGetUrl in the app.config like so:
<serviceMetadata httpGetEnabled="true" httpGetUrl=http://devserver1:8888/MyService />
And now the wsdl works correctly on that server. The problem is that I have to manually set the address in each app.config that gets deployed.
Is there a way to either:
1. Have the wsdl already include everything so that there are no imports?
or
2. Use relative paths in the wsdl import statements?
Or any other suggestions would be appreciated. I have two development servers that the deployment is automated to, if only it weren't for this wsdl problem.
Since this is only for generating the proxy, I suppose I could generate the proxy and distribute it myself, but I'd rather let users generate the proxy themselves.
Thanks!
Daniel
You could set the value of httpGetUrl programatically and set it to an absolute address that includes the machine name of the server of that the services are being hosted on. The import statements in the generated WSDL will then also use the machine name of the server.
If your WCF host is being created for you (for example, you are hosting under IIS) then you will need to create a custom ServiceHostFactory to get access to the ServiceHost. For example:
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;
namespace WebApplication
{
public class TestServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType,
baseAddresses);
ServiceMetadataBehavior metadataBehavior =
new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.HttpGetUrl = new Uri(string.Format(
"http://{0}/WebApplication/TestService.svc",
Environment.MachineName));
host.Description.Behaviors.Add(metadataBehavior);
return host;
}
}
}
You then specify this factory in the .svc file of the service:
<%# ServiceHost Language="C#"
Service="WebApplication.TestService"
CodeBehind="TestService.svc.cs"
Factory="WebApplication.TestServiceHostFactory" %>
If you are creating the WCF host yourself then your code would look something like this:
ServiceHost host = new ServiceHost(typeof(WebApplication.TestService));
ServiceMetadataBehavior metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.HttpGetUrl = new Uri(string.Format(
"http://{0}/WebApplication/TestService.svc",
Environment.MachineName));
host.Description.Behaviors.Add(metadataBehavior);
The answer left above by Daniel Richardson is a good one, and I think that for most people, that would be the preferred solution. However, because of our network layout and the few people that will need to access our server, I am doing something a little different.
I have changed my app.config to have an httpGetUrl that contains "myServiceServer":
<serviceMetadata httpGetEnabled="true" httpGetUrl=http://myServiceServer:8888/MyService />
To use my service, someone has to first add in a host file entry that maps "myServiceServer" to the correct IP address. This works well for our problem because the IP address can't be resolved from any common machine name or IP address. This is because of separated networks that are connected only by VPNs with some kind of NAT going on.
There is apparently a very-well-hidden option built into WCF that supports using the same hostname as the incoming request, which will usually be correct. (I assume the only reason it's not the default is for backwards compatibility, although it probably would have been better if they had made it the default anyway.)
It took much searching and pulling of hair before I found this particular gem (although now that I know the magic word, I found another answer that came from a similar journey to mine).
To enable it, add the following code in the same place where you're adding the ServiceMetadataBehavior:
host.Description.Behaviors.Remove<UseRequestHeadersForMetadataAddressBehavior>();
host.Description.Behaviors.Add(new UseRequestHeadersForMetadataAddressBehavior());
I have a WCF service that gets called from client side JavaScript. The call fails with a Service is null JavaScript error. WebDevelopment helper trace shows that the calls to load the jsdebug support file results in a 404 (file not found) error.
Restarting IIS or clearing out the Temp ASP.Net files or setting batch="false" on the compilation tag in web.config does not resolve the problem
From the browser
https://Myserver/MyApp/Services/MyService.svc displays the service metadata
however
https://Myserver/MyApp/Services/MyService.svc/jsdebug results in a 404.
The issue seems to be with the https protocol. With http /jsdebug downloads the supporting JS file.
Any ideas?
TIA
Figured it out!
Here is the services configuration section from web.config
Look at the bindingConfiguration attribute on the endpoint. The value "webBinding" points to the binding name="webBinding" tag in the bindings and that is what tells the service to use Transport level security it HTTPS. In my case the attribute value was empty causing the webservice request to the /js or /jsdebug file over HTTPS to fail and throw a 404 error.
<services>
<service name="MyService">
<endpoint address="" behaviorConfiguration="MyServiceAspNetAjaxBehavior" binding="webHttpBinding" bindingConfiguration="webBinding" contract="Services.MyService" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webBinding">
<security mode="Transport">
</security>
</binding>
</webHttpBinding>
</bindings>
Note that the bindingConfiguration attribute should be empty ("") if the service is accessed via http instead of https (when testing on local machine with no certs)
Hope this helps someone.
If you still get the same error after all your possible work done. Just add a "AJAX Enabled WCF-Service".
For me the issue was the following; we added MVC to a solution with routing. Our WCF services were not being ignored. I resolved this by adding the following rule (where "WCF" is the folder we keep our services in).
routes.IgnoreRoute("WCF/{*pathInfo}");
Hope that saves somebody a few hours.