Programmatically access WCF EndPoint URL - wcf

I have an IIS hosted WCF service (configured as described in this blog post ... I need to know what the configured endpoint's URL is. For example, given this configuration:
<system.serviceModel>
<services>
<service behaviorConfiguration="mexBehavior" name="Sc.Neo.Bus.Server.MessageProxy">
<endpoint address="http://localhost:9000/MessageProxy.svc" binding="basicHttpBinding"
contract="Sc.Neo.Bus.IMessageProxy" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BusWeb.Service1Behavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior name="mexBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
I'd like to be able to get the value 'http://localhost:9000/MessageProxy.svc' into a string variable in the web application's onstart event.

OK so the short answer is "you can't". The longer answer is you can, kind of, with a bit of work.
If you're hosting this outside of IIS it's actually reasonable to load the configuration section itself and parse it, as you can cast it to the WCF configuration class:
ServiceModelSectionGroup serviceModelGroup =
cfg.GetSectionGroup("system.serviceModel")
as ServiceModelSectionGroup;
Somewhat messy but it does work. The problem comes with IIS - IIS hosted services inherit their address from IIS and will ignore fully qualified addresses in any configuration file.
But you can cheat, you could use a custom service host factory. This does mean changing your service startup code, in either code or the .svc file for IIS. A custom service host factory deriveds from ServiceHostFactory and overrides
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
As you can see you're getting one or more URI objects containing the (potential) addresses of your service. At this point you can store them somewhere (singleton lookup table perhaps) against the service type and then query that elsewhere.
Then in your .svc file you need to change it just a little; for example
<%#ServiceHost Service="MyService.ServiceName"
Factory="MyService.ServiceHostFactory" %>
<%#Assembly Name="MyService" %>

If you are already have an instance of your service proxy created, you can use the following:
public static Uri GetServiceUri(this IMyService proxy)
{
var channel = proxy as IContextChannel;
return
channel != null && channel.RemoteAddress != null ?
channel.RemoteAddress.Uri : null;
}

Related

Is there way to convert just some service methods in a WCF to webMethods? Or add webmethods to an existing WCF? [duplicate]

Background
I have created ASMX web services in the past and have been able to access the service from the web browser and Ajax GET requests using the address convention: MyService.asmx/MyMethod?Param=xxx
I just got started using WCF and created a new web service in my ASP.NET project. It creates a file with the .svc extension such as MyService.svc.
Current Situation
I am able to consume the service using the WcfTestClient that comes with VS2008. I am also able to create my own WCF Client by either adding a service reference in another project or using the svcutil.exe commandline to generate the proxy and config file.
The Problem
When I try to use the service from a browser using MyService.svc/MyMethod?MyParam=xxx, I get a blank page without any errors.
What I have tried
I have already added a basicHttpBinding to the web.config and made it HttpGetEnabled in the behavior configuration. I also added the [WebGet(UriTemplate = "MyMethod?MyParam={MyParam}")] attribute to my operation contract.
I have already followed the information in this other stack overflow question:
REST / SOAP EndPoints for a WCF Service
However, I either get a blank page or an HTTP 404 Error after following those steps. There's nothing special about the code. I am just taking in a string as a parameter and returning "Hello xxx". This is a basic "Hello WCF World" proof-of-concept type thing.
UPDATE - Here's the relevant code
[ServiceContract]
public interface IMyService
{
[WebGet(UriTemplate = "MyMethod/MyParam={MyParam}")]
[OperationContract]
string MyMethod(string MyParam);
}
Web.Config - system.serviceModel Section
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyService">
<endpoint address=""
binding="wsHttpBinding" contract="IMyService" />
<endpoint address="MyService.svc"
binding="basicHttpBinding" contract="IMyService" />
<endpoint address="mex"
binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
Looking at your web.config serviceModel section, I can see that you need to add a webHttpBinding and associate an endPointBehavior that includes webHttpGet.
Your operation contract is correct. Here's how your system.serviceModel config section should look in order for you to be able to consume the service from a GET HTTP request.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyService">
<endpoint address="ws" binding="wsHttpBinding" contract="IMyService"/>
<endpoint address="" behaviorConfiguration="WebBehavior"
binding="webHttpBinding"
contract="IMyService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
Be sure to assign a different address to your wsHttpBinding endpoint, otherwise you will get an error saying that you have two endpoints listening on the same URI.
Another option is to leave the address blank in the wsHttpBinding, but assign a different address to the webHttpBinding service. However, that will change your GET address as well.
For example, if you assign the address as "asmx", you would call your service with the address "MyService.svc/asmx/MyMethod?MyParam=xxxx".
The normal WCF requests are always SOAP requests - you won't be able to get this going with just your browser, you'll need the WCF Testclient for that.
There is an add-on for WCF called the WCF REST Starter Kit (which will also be included in WCF 4.0 with .NET 4.0), which allows you to use GET/POST/PUT/DELETE HTTP commands to query WCF services and such. You need to write your services specifically for REST, though - you can't have SOAP and REST on the same service call.
Marc
As marc_s says, the REST Starter Kit can help, but you should also be aware that .NET 3.5 has support for REST services directly in it. It's not quite as complete as what you can do with the starter kit, but it is useful.
The way it works is that you put a [WebGet] attribute on your operations to indicate where in the URL the various parameters should come from:
[WebGet(UriTemplate = "helloworld/{name}")]
string Helloworld(string name);
See this portal for tons of information.
Note, you can have the same service exposed as both SOAP and REST if you specify multiple endpoints/bindings in the configuration.

Could not find default endpoint element that references contract

I know this has been beaten to death, but I cannot get this to work as it should.
I have a WCF service with several contracts.
They all work fine when calling them directly e.g. http://merlin.com/CompanyServices/CompanyWcfService.svc/Get_Document_Dates_Received/223278
I have used this WCF service successfully on InfoPath Forms and Nintex Workflows.
Now I create a simple ASP.Net application, such as was done in http://www.webcodeexpert.com/2013/04/how-to-create-and-consume-wcf-services.html.
I was able to add a service reference as described in the article.
I added a button the form, and added the following code in the Button1_Click event:
protected void Button1_Click(object sender, EventArgs e)
{
ServiceReference1.CompanyWcfServiceClient x = new ServiceReference1.CompanyWcfServiceClient();
var result = x.Get_Document_Dates_Received("223278");
}
when I click on the button I get the error:
"Could not find default endpoint element that references contract 'ServiceReference1.ICompanyWcfService' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element."
So I tried adding the following to the web.config: (copied directly from the web.config file of the CompanyWcfService.
<system.serviceModel>
<services>
<service name="CompanyWcfServices.CompanyWcfService" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="webHttpBinding" contract="CompanyWcfServices.ICompanyWcfService" behaviorConfiguration="webHttpEndpointBehavior" >
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange">
</endpoint>
</service>
</services>
<bindings>
<webHttpBinding>
<binding>
<security mode="None">
</security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name ="webHttpEndpointBehavior">
<webHttp helpEnabled ="true" faultExceptionEnabled="true" automaticFormatSelectionEnabled="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
I get the same exact error, there has to be something else going on.
I finally gave up and called the service like this:
HttpWebRequest request = WebRequest.Create(#"http://merlin/Companyservices/CompanyWcfService.svc/Get_Document_Dates_Received/223278") as HttpWebRequest;
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse response = null;
var result = "";
try
{
response = request.GetResponse() as HttpWebResponse;
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
result = reader.ReadToEnd();
}
}
}
catch (Exception ex)
{
result = "";
}
I have spent hours reading posts and most of them suggest to copy the config information to the web.config file. This seems problematic to me (besides the fact that it doesn't seem to work). What if I need to consume a third party WCF service? Do I have to request the config information from the third party? And Visa Versa, if I create a WCF service designed to be consumed by third parties, do I need to provide them the config file as well?
The error indicates that you don't have an endpoint defined in the client configuration section. When you add the service reference to your project it should create the client section for you. If not then in the web.config for your app within the system.serviceModel section add the following
<client>
<endpoint
name="CompanyWcfService_webhttpBinding"
address="http://merlin.com/CompanyServices/CompanyWcfService.svc"
binding="webHttpBinding"
contract="CompanyWcfServices.ICompanyWcfService"
behaviorConfiguration="webHttpEndpointBehavior"
/>
</client>
If we have layered architecture make sure to
1) add app.config in "all projects"
2) add service config details in all app.config
3) run the project
If your project is referencing a library and trying to use the WCF functions from the functions of that library, then you can try copying the client endpoint from the project config file to the dll's config file. Some thing like this happened to me a while ago as the library that I referenced in the project would not use the project config file (in which the client end point was configured since the service was being referenced there) but its own so the result was the system could not find the endpoint configurations.
In my case I had a WPF project referencing an external UserControl which had a service reference. I had to add the service reference to the main project as well.
Adding binding and client values from app.config to default web.config resolved my issue.
Actually the trick to this one was to use the svcutil.exe to create the proxy. I had been trying to create the proxy through Visual Studio "Add Service" wizard. Once I did that, the configuration was a breeze.
SvcUtil.exe
When it comes down to WCF, it typically requires the configuration to be defined within the config file of the executable that calls it.
Thus, if you are unfortunate enough to having to call a WCF DLL from within a VB6 program (as may be the case when using a COM-interop .NET module, for example), and you need to debug the VB6 program, then you will need to create a VB6.exe.config file in the directory where VB6.exe is located.
Failing to do the above may cause a "Could not find default endpoint element that references contract".
As a workaround, one can load the dll's config file at runtime and then call the constructor of the used Service with a Binding and an EndpointAddress as parameters (obtained from the dll's config).

WCF SOAP + REST/Json service without .svc - do I have twice the number of service factories?

Ok, so I wanted to have a single service exposed as a SOAP as well as REST (Json) end point. Since it's off the "WCF Service Application" template, I have a web.config and I added the following into the ...
web.config
<configuration>
...
<system.serviceModel>
<services>
<service name="MySvcClass">
<endpoint address="" binding="webHttpBinding" contract="MySvcInterfaceClass" behaviorConfiguration="restBehavior" />
<endpoint address="soap" binding="basicHttpBinding" contract="MySvcInterfaceClass" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="restBehavior">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
...
</configuration>
Assuming the MySvcClass class is implemented within \MySvcClass.svc, the above exposes ...
Original Endpoints
localhost\MySvcClass.svc\ (rest endpoint)
localhost\MySvcClass.svc\mex (Metadata Exchange to the use the SOAP end point below)
localhost\MySvcClass.svc\soap (soap endpoint)
So far, so good (I think!).
Then I wanted to get rid of the ugly ".svc" seen in the paths above. So I followed this MSDN blog post and had this in my ...
global.asax
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("MySvcClass", new WebServiceHostFactory(), typeof(MySvcClass)));
}
Interestingly, when I put a breakpoint inside Application_Start, VS2010 doesn't hit this particular breakpoint - even when I stop->start debugging or stop->start the IIS application pool. Bizarre! Anyway, back to the point, I can now access the services the above listed endpoints AND
Cleaner Endpoints
localhost\MySvcClass\ (rest endpoint)
localhost\MySvcClass\mex (Metadata Exchange to the use the SOAP end point below)
localhost\MySvcClass\soap (soap endpoint)
Questions
Am I having TWO service factories? One from the web.config and the other from the global.asax? If yes, how can I avoid it while still having clean URLs (without .svc). I don't really need the ones with .svc in the path ...
I dislike cluttered web.configs, so is there any way I can move the above SOAP and REST configuration from the XML (web.config) into code (eg global.asax?) ? I know how to move the REST only end point - wipe out in the web.config, leave global.asax as is. However doing that kills the SOAP endpoint.
[Update]
I had tried URL rewrites too but this killed the SOAP endpoint while keeping the REST endpoint alive. Wht I did was : Used MS's URL Rewrite 2.0 module with this in the web.config
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<rewrite>
<rules>
<rule name="RemoveSvcExt" stopProcessing="true">
<match url="^MySvcClass(.*)$" />
<action type="Rewrite" url="MySvcClass.svc{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
However, this leaves the web app in some inconsistent state because there are some parts which still stick to the .svc URLs. eg: the HTML help page at the service endpoint shows svcutil.exe http://localhost/MySvcClass.svc?wsdl Even the WSDL at the clean location at http://localhost/MySvcClass?wsdl makes references to http://localhost/MySvcClass.svc inside it - this effectively kills the SOAP endpoint.
That's why I think ($0.02) the rewrite is just a kludge. Sigh, at this point I'm fighting with the framework to get stuff done. And it feels such a time burner ...
For services without .svc search for file less activations in wcf
http://www.a2zdotnet.com/View.aspx?Id=188
I have a REST project that as both REST and SOAP service being exposed. Now I placed an .svc file for the SOAP service to be accessed by some clients.
The below screenshot gives the folder structure of my project, the route configuration in global.asax, Output accessing the Rest Service and accessing the .svc file (SOAP service). To remove the .svc extension use the URL rewrite module.
Please find my web.Config (My application is hosted on IIS):
Please find my class that implements my interface ISampleService:

Using WCF Web apis (REST) to support Streamed data

I have the following problem. Let me describe the steps I took so far...
I created a new WCF Service Application in Visual Studio
I then updated the project via Nuget to get the latest web http libs (webapi.dll)
I then created a service method that looks like this
`
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method="POST", UriTemplate="{value}")]
string GetData(int value, Stream inputDocument);
}
`
Now attempting to view the my .svc in the browswer results in an error that says "For request in operation GetData to be a stream the operation must have a single parameter whose type is Stream"
I know this is an issue with configuration, I just don't know what needs to change in web.config Mind you, this seems to have been a common problem in WCF before the new HTTP support, I'm somewhat surprised that this doesn't work out of the box with the new APIs.
Any pointers?
Thanks
[EDIT] I've included my config...
<system.serviceModel>
<services>
<service name="MyService.Service" behaviorConfiguration="serviceBehaviour">
<endpoint behaviorConfiguration="endPointBehaviour" address="" binding="webHttpBinding" contract="MyService.IService"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding transferMode="Streamed" name="webHttpBinding" />
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="endPointBehaviour">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="serviceBehaviour">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
You are mixing up the new WCF Web API stuff with the old WCF REST stuff. Take a look at the HttpHelloResource sample as the simplest example of how to run a Web API service under IIS, or my blog post for an even simpler example of a service running in a console.
As for accepting a stream I think your simplest option would be an operation like this:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method="POST", UriTemplate="{value}")]
string GetData(int value, HttpRequestMessage request);
}
and you can get the stream by doing
var stream = request.Content.ContentReadStream
Ok, so it seems the error message was taking me down the wrong path. I think that error message needs to be far more descriptive. Basically there's nothing wrong my code at all, it just doesn't make sense to point my browser to the .svc file as the service is not quite a WCF service. I learmt this by going ahead and accessing the service via code. And it works. Thanks for the help

WCF base address not found

My service can work with normal WCF calls, but to expose metadata (the wsdl file) I have to change configuration in such a way the normal WCF host fails.
I've spend countless hours on google trying to solve this, big problem there is that hosting a service inside a website is never discussed (yes this is different).
requirements:
Runs in an existing web site
Use sessions
Operable with Java, and as much .net versions as possible.
Expose metadata (wsdl will be enough)
edits:
IIS cannot be used
I'm using .NET 4 and WCF 4.
In this configuration the metadata can be reached (through the wsdl file) but when trying to host the normal wcf endpoints I get and InvalidOperationException:
Could not find a base address that matches scheme http for the endpoint with binding WSHttpBinding. Registered base address schemes are [].
So the base address is ignored.
But when I supply full addresses to the endpoints (simply copy the base address in front of the current address) the normal WCF calls work fine, but when trying to access metadata I get the following error:
No protocol binding matches the given address 'http://localhost:8080/Functionality'.
Protocol bindings are configured at the Site level in IIS or WAS configuration.
Here is the web.config serviceModel section, I made a small test web site just for testing this, but it would be to much to post all of it here, if you send me a pm though I will e-mail it to you.
<system.serviceModel>
<services>
<service behaviorConfiguration="metadataSupport" name="MyStuff.TestWithMetadata">
<endpoint address="Functionality" binding="wsHttpBinding" name="FunctionalityBinding"
contract="MyStuff.ITestWithMetadata" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="metadataSupport">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="metadataSupport">
<!--Navigate with browser to httpGetUrl for the wsdl file-->
<serviceMetadata httpGetEnabled="true" httpGetUrl="Metadata" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false">
<serviceActivations>
<add relativeAddress="TestWithMetadata.svc" service="MyStuff.TestWithMetadata" />
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
If anyone has any ideas on how to solve this, please help out.
When you host your service in IIS (which I assume from your requirement "Runs in an existing web site"), then your base address in the config is moot - it will not be used at all.
When hosting in IIS, your service address is determined by:
your server name
possibly a port number
the virtual directory (and possibly subdirectories thereof) where the *.svc file lives
the *.svc file itself (including extension)
So it might be something like:
http://MyServer:7777/ExistingWebApp/TestWithMetadata.svc
or whatever it is that you have in your case.
You seem to be using .NET 4 and WCF 4 (never mentioned that.....) and in that case, you could skip the *.svc file altogether by adapting your config entry:
<serviceHostingEnvironment multipleSiteBindingsEnabled="false">
<serviceActivations>
<add relativeAddress="MyService" service="MyStuff.TestWithMetadata" />
</serviceActivations>
</serviceHostingEnvironment>
In this case, the value of relativeAddress= becomes the service address (within the virtual directory this web.config lives in) - so your service address would be something like:
http://MyServer:7777/ExistingWebApp/MyService
No need for a *.svc file at all in this situation.
Turned out I should use httpGetUrl link to get the metadata, instead of the .svc file, with that the base address can be ignored.
I also moved this test stuff to the actual web site and got tons of problems with zero endpoints being loaded. That was caused by the service reference in serviceActivations not set to the full service name (needs to have namespace included).
I accepted marc's answer because he did help me along and to prevent this question from popping up in unanswered.