Using WCF Web apis (REST) to support Streamed data - wcf

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

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 3.5 running SOAP and REST services side by side in IIS

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.

WCF Multiple Endpoints Under IIS7

I have a simple WCF service that we are developing... We are hosting in IIS7 on WinServer2k8 (though i cant get it to work in IIS7 on Win7 either)
I want multiple endpoints for the same service contract but have the endpoints behave differently. For example I want one endpoint to return data as XML and another to return data in SOAP messages.
Here is my web.config
<system.serviceModel>
<services>
<service name="MemberService">
<endpoint address="soap" binding="basicHttpBinding" contract="IMemberService" />
<endpoint address="xml" binding="webHttpBinding" contract="IMemberService" behaviorConfiguration="xmlBehavior" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="xmlBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
In my service contract i have a method defined as:
[OperationContract]
[WebGet(UriTemplate = "members/{id}")]
Member GetMember(string id);
When I deploy to IIS (on localhost) and make a request (with fiddler) to http://localhost/MemberService.svc/xml/members/memberid I receive a 404 error, also a 404 with http://localhost/MemberService.svc/soap/
However, http://localhost/MemberService.svc/members/memberid works and serializes the data as expected. We want to add the functionality of JSON in the near future as well, we thought it would be another endpoint with a different behavior. My web.config is modeled after a post i found on here
Following this tutorial....
I was able to quickly deploy the webservices. Then using fiddler I could change the content-type of the request to/from "text/xml" and "text/json" and the service would automatically return the data in the correct format.

Programmatically access WCF EndPoint URL

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;
}