Asp.net Self Hosted WCF Service WSDL with relative paths - asp.net

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());

Related

ASP.Net Web.config client endpoint name

I have a service that has references to other services with some of them being references to different environments of the same service (e.g. prod/test).
I am using the #if precompile directive to include different versions of these references with the using statement. Example:
#if Debug
using ServiceTest
#else
using ServiceProd
#endif
In the Web.config file I have two child nodes inside the <client> node. Example:
<client>
<endpoint address="http://test.domain.com/Service"
binding="basicHttpBinding" bindingConfiguration="Service"
contract="ServiceTest" name="Service" />
<endpoint address="http://prod.domain.com/Service"
binding="basicHttpBinding" bindingConfiguration="Service"
contract="ServiceProd" name="Service" />
</client>
Is the above part of the Web.config valid or not? More precisely, can there be any side-effects because of having two endpoints with the same name and binding configurations? The main concern is having a wrong endpoint called (e.g. calling prod endpoint instead of test or the other way around).
Any guidance and advice regarding the above will be really appreciated.
Is the above part of the Web.config valid or not?
Every time when you run your application CLR reads web.config file and deserialize it as an object. To deserialize XML it's uses classes declaration in "configSections" section of your config file.
So, the answer "valid or not" depends on implementation of "client" configuration section. I believe this should be part of your application or code from nuget library. This is why we can't answer you with confidence.
More precisely, can there be any side-effects because of having two endpoints with the same name and binding configurations?
Frankly, I don't understand how this should work. In what manner 3rd library should know that it needs to load first but not second client endpoint?
Possible solutions.
You can use web.config transformation. There are tons of resources about this feature of .NET Framework.
The simplest scenario would be to substitute endpoint address keeping the same endpoint name.
Another common scenario is to give different endpoint names and keep "alive" endpoint name in AppSettings. In this case your code should resolve endpoint name first and then actual endpoint address.

ASP.NET: Exposing same core functionality as SOAP _and_ REST interfaces

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.

There was no endpoint listening and Error 404

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 ...

Making WCF service work

I'm using Visual Studio Express 2010, I've created WCF service called OperatorService.svc. Two files were added to my App_Code IOperatorService.cs and OperatorService.cs.
My web.config was updated with
<system.serviceModel>
<services>
<service name="OperatorService">
<endpoint address="https://ssl.mysite.com/WCF/OperatorService"
binding="ws2007HttpBinding"
bindingConfiguration="SecurityByTransport"
contract="IOperatorService" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="SecurityByTransport">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
Now when i'm trying to access this service online, get an error to create EndPoint but i can't figure our how to create EndPoint especially when WCF address is SSL HTTPS: enabled website.
Can someone help meh?
The endpoint address, if you are IIS hosting, should be either left empty or a relative address.
WCF services can have base addresses. A base address defines a core part of the address space that the service can listen on and endpoints are defined relative to that base address. If you leave the address empty then the endpoint listens on the base address.
When you are self hosting you can specify a base address in a couple of different ways: in the ServiceHost constructor or in the config file. However, if you are IIS hosting then the base address is already a given - it is the location of the .svc file
As far as HTTPS goes, if you say that you are using transport security then the base address will automatically map to HTTPS as long as that is enabled as a protocol in web application in IIS manager. However, if you are using the Visual Studio Web Development Server (aka Cassini) then that does not support SSL
Launch the WCF config tool (SvcConfigEditor.exe, it is a available in the menu of Visual Studio, otherwise the path should be C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin) and open your config file, it is GUI tool to help you make a correct config.
The error in the config file is an incomplete endpoint element, you need to specify some attributes on the endpoint to make it work. The easiest way is to use the config tool, but of course it can be hand written. MSDN has a reference on the syntax.

WCF Endpoints & Binding Configuration Issues

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="" ... />

Resources