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.
I have created and hosted a service http://www.royalbatteriesws.somee.com/Default.svc
I want to test my service for its functionality.
I have added a service reference in my Asp.Net web site called RoyalBatteriesWS
As no endpoint was added automatically in the web.config file, I had to add it manually.
web.config file:
System.ServiceModel and ServiceBehaviors sections:
<service name="RoyalBatteriesWS" behaviorConfiguration="HelloServiceBehavior">
<endpoint
address ="http://www.royalbatteriesws.somee.com/Default.svc"
binding="basicHttpBinding"
contract="RoyalBatteriesWS.IService">
</endpoint>
</service>
<serviceBehaviors>
<behavior name="HelloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
I get an exception
Could Not find default Endpoint that references contract 'RoyalBatteriesWS.Iservice' in the ServiceModel client configuration section
Please can anybody point out my mistake?
I went through previous posts and I couldn't find what works for me.
Thanks
The name="..." attribute of your <service> must match exactly the fully-qualified name of the .NET class that implements that service.
This means: it has to be the exact class name and all namespaces of that class.
So most likely, your service class is called RoyalBatteriesWS - but there's a good chance it's stored in some namespace - so add that namespace to that attribute and you should be good to go!
On the other hand, since you said you added a service reference - do you really want to configure a service here - that's the server-side of things (where the actual service class is coded).
Don't you want to configure a class that consumes / uses that service - a client ??
If so - use the <client> tag (instead of <service>):
<client name="RoyalBatteriesWS">
<endpoint
address ="http://www.royalbatteriesws.somee.com/Default.svc"
binding="basicHttpBinding"
contract="RoyalBatteriesWS.IService">
</endpoint>
</client>
I have been using stackoverflow.com as a resource as a professional programmer for years now. I would say 8 out of 10 times when I search for something on google, I get pointed to a question and answer here, and I am always relieved when that happens, because I know I am about to find the information I need.
I have been pulling my hair out trying to figure (what I think to be) a simple problem out, concerning setting up a routing service using WCF technology. I have browsed through the questions with similar titles here, and I have consulted a great many resources (both actual books on the subject, as well as websites) trying to figure this out, to no avail.
In a nutshell, I want to setup a system with the following layout:
{client}<-basicHTTP->{portal/router}<-fullWCF-WS*->{end-point-services1..n}
client: gets service reference to portal, able to call functions at end-point service
portal/router: gets requests from client, and sends them on to end-point services in a multi-cast setup
end-point-services1..n: gets request from client, routed through portal, processes request to search for things, and either responds, or logs data in a database to be checked later
I am, 100%, able to get a routing service up and running. The most successful models I have been able to follow, were outlined in the "What's new in WCF4: exercises 8 & 9, content bridging & routing" (msdn.microsoft.com/en-us/gg465212) and "Hello World with the Routing Service" (msdn.microsoft.com/en-us/library/dd795218.aspx) But I have used bits and pieces from all of the sources I have consulted (listed below).
Basically, what is frustrating me, is that I want a client (a 3rd party) to be able to just add a web service reference to the portal service (or, worst case scenario, use the svcutil.exe method), and be done with setup on their part. With that reference, they will have references to all the functions/methods they would want to call in all scenarios. The models I have looked at that do this require 2 references, one to the actual service, and one to the router, and then force the client to specifically call the router in their setup. None of my other attempts to make this particular setup work, have worked.
Your help with this would be greatly appreciated.
Here is a simplified version of my working model that almost is doing what I want:
(note, all services are being hosted in IIS)
PORTAL SERVICE (and IIS Host)
Portal.svc:
<%# ServiceHost Service="System.ServiceModel.Routing.RoutingService, System.ServiceModel.Routing, version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
Web.config:
<configuration>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
...
</bindings>
<client>
<endpoint address="http://searcher1/Searcher.svc/general" binding="basicHttpBinding" contract="*" name="regularSearchServiceEndpoint" />
<endpoint address="http://searcher2/Searcher.svc/general" binding="basicHttpBinding" contract="*" name="regularSearchServiceEndpoint2" />
</client>
<behaviors>
...
</behaviors>
<routing>
<filters>
<filter name="MatchAllFilter" filterType="MatchAll" />
</filters>
<filterTables>
<filterTable name="filterTable1">
<add filterName="MatchAllFilter" endpointName="regularSearchServiceEndpoint" backupList="backupList1" priority="0"/>
</filterTable>
</filterTables>
<backupLists>
<backupList name="backupList1">
<add endpointName="regularSearchServiceEndpoint2"/>
</backupList>
</backupLists>
</routing>
<services>
<service behaviorConfiguration="routingConfiguration" name="System.ServiceModel.Routing.RoutingService">
<endpoint address="general" binding="basicHttpBinding" name="routerEndpoint1" contract="System.ServiceModel.Routing.IRequestReplyRouter" />
</service>
</services>
</system.serviceModel>
</configuration>
SEARCH SERVICE
ISearch.cs:
namespace SearchService
{
[ServiceContract]
public interface ISearch
{
[OperationContract]
string Ping();
[OperationContract]
string searchByInput(string input);
}
}
App.config:
<configuration>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<bindings>
<basicHttpBinding>
...
</basicHttpBinding>
<customBinding>
...
</customBinding>
</bindings>
<client>
...
</client>
<services>
<service name="SearchService.Search">
<endpoint address="general" binding="basicHttpBinding" contract="SearchService.ISearch" name="SearchService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/Design_Time_Addresses/SearchService/Service1/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
SEARCH SERVICE HOST
Search.svc:
<%# ServiceHost Service="SearchService.Search" %>
Web.config:
<configuration>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
<basicHttpBinding>
<!--copied over from SearchService.App.config-->
</basicHttpBinding>
<customBinding>
<!--copied over from SearchService.App.config-->
</customBinding>
</bindings>
<client>
<!--copied over from SearchService.App.config-->
</client>
<services>
...
</services>
<behaviors>
...
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
CLIENT (where it all goes wrong)
Only way I have been able to get it to do what I want, is to add a web service reference to the searcher service (named "remotehost"), and then manually add client endpoints to the app.config file for the router, and force the client code to use that, INSTEAD of the direct link it already has to the searcher
Main.cs:
namespace Client
{
public partial class Main : Form
{
remotehost.SearchClient proxy;
public Main()
{
InitializeComponent();
proxy = new remotehost.SearchClient("RouterService");//("BasicHttpBinding_ISearch")
}
private void button1_Click(object sender,EventArgs e)
{
string response = string.Empty;
//uses method exposed by the SearchService service
response = proxy.Ping();
MessageBox.Show("Response from remote service:\n" + response
"Ping Response",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
}
}
App.config:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
...
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://searcher1/Searcher.svc/general" binding="basicHttpBinding" bindingConfiguration="SearchService" contract="remotehost.ISearch" name="SearchService" />
<!--I manually added this-->
<endpoint address="http://portal/Portal.svc/general" binding="basicHttpBinding" contract="remotehost.ISearch" name="RouterService" />
</client>
</system.serviceModel>
</configuration>
I want to emphasize, this all WORKS, but it doesn't work the WAY I want it to. I am fairly certain I can push/pull/cajole this into the elegant setup I am envisioning, but I can't seem to find a resource or guide that will walk me through it for the first time.
Help?
Sources I have consulted, before coming here:
Learning WCF: A Hands-on Guide, by Bustamante, Michele Leroux {978-0-5961-0162-6} (read cover to cover, and did all exercises)
Windows Communication Foundation 4: Step By Step {978-0-7356-4556-1} (focused on chapter 14: Discovering Services and Routing Messages)
msdn.microsoft.com/en-us/library/ms734712.aspx {WCF: Getting Started Tutorial}
msdn.microsoft.com/en-us/gg465212 {what's new in WCF4: exercises 8 & 9, content bridging & routing}
codeproject.com/Articles/146835/How-to-create-scalable-services-with-WCF-4-0-Route {How to create scalable services with WCF 4.0 Router and Discovery services}
msdn.microsoft.com/en-us/library/dd795218.aspx {Hello World with the Routing Service}
msdn.microsoft.com/en-us/library/ee517421.aspx {routing}
msdn.microsoft.com/en-us/library/ee517423.aspx {routing service overview}
msdn.microsoft.com/en-us/library/ee517418.aspx {routine service features}
msdn.microsoft.com/en-us/library/ee517422.aspx {routing intro}
msdn.microsoft.com/en-us/library/ee517420.aspx {routing contracts}
msdn.microsoft.com/en-us/library/bb332338.aspx {wcf routing}
msdn.microsoft.com/en-us/library/ms730158.aspx {more wcf routing}
msdn.microsoft.com/en-us/library/ee354381.aspx {more wcf routing}
dandcohen.wordpress.com/2010/03/02/wcf-4-routing-service-multicast-sample/ {WCF 4 Routing Service Multicast sample}
UPDATE: 2012-04-28:
I figured out a way to do what I wanted. It still isn't as elegant as I wanted, but it get the job done and has allowed me to move forward.
Basically, take the interface from the main service, and implement it in a new service, call it router or portal, or whatever. In the new router/portal service, add a new service reference to the main service.
Now, both services are using the same interface, and have the same signatures for all their methods, so you can then just give the portal/router service wsdl to the 3rd party client, and only allow your portal/router service to communicate with the main service.
Additionally, if you have more than one main service, you can use the portal/router service to decide which of the main services to send requests to, using multiple service references to them, and proxies to send the jobs onward. It works really well.
It is basically a manual front-end routing service, but the beauty is, the detailed work can be done in the main services on a threading model, while the gatekeeping work can be done at the portal/router, so only actual requests are sent to the main services, allowing them to only do work, and the portal services to decide how or if they get that work. The next step I want to add it automatic discovery of new services, but for now, manual configuration is working fine.
I can post the source code for what I came up with, if anyone wants to see it and requests it.
The fundamental problem is that the router knows nothing about the service contract that the service is using - it uses a universal contract (one which uses the Message type). Therefore, there is no way for the router to auto-generate the metadata for the client.
What you will need to do is provide the metadata yourself, maybe as a static WSDL document, with the correct addresses in it and point clients to this
I know that similar question was asked here :
Running SOAP and RESTful on the same URL
Hosting WCF soap and rest endpoints side by side
but didn't find an answer to my problem.
I have two custom servicehostfactories that enables Dependency Injection :
public class StructureMapSoapServiceHostFactory : ServiceHostFactory
public class StructureMapRestServiceHostFactory : WebServiceHost2Factory
The implementation details are not important here.
Then I definied two endpoints in web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexGet">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<mexHttpBinding>
<binding name="mexHttpBinding" />
</mexHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="mexGet" name="ServiceImplementation.ServiceCategory">
<endpoint address="rest"
binding="webHttpBinding"
contract="Contracts.ServiceContracts.Mobile.IServiceCategory"
behaviorConfiguration ="jsonBehavior"/>
<endpoint address="soap"
binding="basicHttpBinding"
contract="Contracts.ServiceContracts.Mobile.IServiceCategory" />
<endpoint name="mexHttpBinding"
address="mex"
binding="mexHttpBinding" bindingConfiguration="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Then I created two .svc files for each custom host factories :
ServiceCategoryRest.svc
ServiceCategorySoap.svc
I don't like it. What I would like to do is to have URL in that style :
REST : http://server:port/rest/categories/{id} which mapps to the implementation of my ServiceCategory.GetCategory(int id)
SOAP : http://server:port/soap/GetCategory?id=someId
My questions are. Do i need different svc files to activate host services ? If I need there two .svc files, how can I achieve the URI above ? I'm afraid that I should configure IIS rewriting or something but would like to avoid that.
Thanks in advance for your help.
Thomas
You can achieve what you're looking for with service routes - part of ASP.NET routing, available from ASP.NET 3.5 SP1 on up.
Check out these resources:
RESTful WCF Services with No svc file and no config
Drop the Soap: WCF, REST, and Pretty URIs in .NET 4
making a WCF REST stand-alone service exe from scratch – part 1 of 4, creating the minimal bare service
Using Routes to Compose WCF WebHttp Services
In .NET 3.5 SP1, you need to add some extra infrastructure to your web.config (web routing module etc.) - while in .NET 4, this is all already built in.
After few searches I found out that in fact I don't need two different .svc files and two different ServiceHostFactories.
I kept only the StructureMapRestServiceHostFactory : WebServiceHost2Factory and ServiceCategoryRest.svc which handles well requests in REST mode and call in RPC-SOAP mode.
So if you want to run side by side the REST and the SOAP you can do it only with WebServiceHost2Factory.
If then you want to get rid of the .svc part from the URL, please read the Rick Strahl post west-wind.com/weblog/posts/570695.aspx.
I have an existing web service (ASMX) that needs to be exposed as WCF as well. ASMX must remain and preferably with no change on the client. As per this I have configured as follows. The service layer is generated with CodeSmith and whilst I didn't write these services I know they are fine as they have been used in the wild for many years. The names have been changed to protect the innocent .. grin.
In the service layer there is an XXX.YYY.MyService class generated by CodeSmith which is double decorated with
[ServiceContract( Namespace = "http://XXX.YYY" )]
and
[WebService( Namespace = "http://XXX.YYY", Name = "MyService" )]
I have also created an empty interface XXX.YYY.IMyService which is implemented by MyService. At this point I can consume the ASMX service with no issues.
Now I add a .svc file to the service layer which contains ...
<%# ServiceHost Language="C#" Debug="true" Service="XXX.YYY.MyService" %>
... and I configure the service layer's web.config with ...
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="XXX.YYY.MyService">
<endpoint binding="basicHttpBinding" contract="XXX.YYY.IMyService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
If I build and then try and make a service reference in Visual Studio 2010 to the service, I see both .ASMX and .SVC versions of MyService. Expanding the .svc branch in the Add Service Reference dialog results in an error referring to an empty XML document.
If I examine the event log I get ...
WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/39449526
Exception: System.ServiceModel.ServiceActivationException: The service '/System/MyService.svc' cannot be activated due to an exception during compilation. The exception message is: The contract name 'XXX.YYY.IMyService' could not be found in the list of contracts implemented by the service 'MyService'.. --->
... but MyService is marked as implementing IMyService ...
public partial class MyService : IMyService
I have also tried changing the contract attribute for the service to MyService instead of the interface. That works but for the client code breaks as any attempt to create an instance of the service fails as it is now an interface.
I hope that makes sense. Please feel free to ask anything extra. I have tried to be as detailed as possible.
(No IIS involved .. this is purely in Visual Studio 2010).
Thanks.
Your code implements IMyService are you sure that it is XXX.YYY.IMyService.
The answer for me was to move the [ServiceContract] and [OperationContract] declarations to the interface. This has fixed the issue for me.
HTH.