I'm just getting into creating some WCF services, but I have a requirement to make them backward compatible for legacy (.NET 1.1 and 2.0) client applications.
I've managed to get the services to run correctly for 3.0 and greater clients, but when I publish the services using a basicHttpBinding endpoint (which I believe is required for the compatibility I need), the service refactors my method signatures. e.g.
public bool MethodToReturnTrue(string seedValue);
appears to the client apps as
public void MethodToReturnTrue(string seedValue, out bool result, out bool MethodToReturnTrueResultSpecified);
I've tried every configuration parameter I can think of in the app.config for my self-hosting console app, but I can't seem to make this function as expected. I suppose this might lead to the fact that my expectations are flawed, but I'd be surprised that a WCF service is incapable of handling a bool return type to a down-level client.
My current app.config looks like this.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="MyServiceTypeBehaviors" Name="MyCompany.Services.CentreService.CentreService">
<clear />
<endpoint address="http://localhost:8080/CSMEX" binding="basicHttpBinding" bindingConfiguration="" contract="IMetadataExchange" />
<endpoint address="http://localhost:8080/CentreService" binding="basicHttpBinding" bindingName="Compatible" name="basicEndpoint" contract="MyCompany.Services.CentreService.ICentreService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Can anyone advise, please?
Ah, this is killing me! I did this at work about 3 months ago, and now I can't remember all the details.
I do remember, however, that you need basicHttpBinding, and you can't use the new serializer (which is the default); you have to use the "old" XmlSerializer.
Unfortunately, I don't work at the place where I did this anymore, so I can't go look at the code. I'll call my boss and see what I can dig up.
OK, we needed to resolve this issue in the short term, and so we came up with the idea of a "interop", or compatibility layer.
Baiscally, all we did was added a traditional ASMX web service to the project, and called the WCF service from that using native WCF calls. We were then able to return the appropriate types back to the client applications without a significant amount of re-factoring work. I know it was a hacky solution, but it was the best option we had with such a large legacy code-base. And the added bonus is that it actually works surprisingly well. :)
You do have to use the XmlSerializer. For example:
[ServiceContract(Namespace="CentreServiceNamespace")]
[XmlSerializerFormat(Style=OperationFormatStyle.Document, SupportFaults=true, Use=OperationFormatUse.Literal)]
public interface ICentreService {
[OperationContract(Action="CentreServiceNamespace/MethodToReturnTrue")]
bool MethodToReturnTrue(string seedValue);
}
You have to manually set the operation action name because the auto-generated WCF name is constructed differently from the ASMX action name (WCF includes the interface name as well, ASMX does not).
Any data contracts you use should be decorated with [XmlType] rather than [DataContract].
Your config file should not need to change.
Related
I'm just getting familiar with WCF and I have to add additional functionality to a working web service at work. As I feel the need to be able to test the functionality before deploying it, I decided to build a test client. Here comes the problem.
I created a Console Application just for the purpose of a test client and tried to add a Service Reference through the provided WSDL but it didn't work. There was no config file created.. I tried first the "Add Service Reference" option in VS and when it didn't work, I tried creating the Proxy and Config files with svcutil.exe...
Just the proxy class gets created... When I try to instantiate a "client object" from that class, the following Exception is thrown: "Could not find default endpoint element that references contract 'eOrderingService.IeOrderingService' 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."
As this is a working service (a Czech company is using it), apparently there must be a way to create a web.config or app.config even manually but I have no idea where to start.. As I said I'm just getting familiar with WCF so I started looking online but most of the problems connected somehow to my issue were in different parts of the already created Config files.. I managed to bypass that exception adding the following to app.config:
<system.serviceModel>
<services>
<service name="eOrderingService" >
<endpoint
address="http://localhost:61472/eOrderingService.svc"
binding="webHttpBinding"
contract="eOrderingService.IeOrderingService" >
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint
address="http://localhost:61472/eOrderingService.svc"
binding="webHttpBinding"
bindingConfiguration=""
contract="eOrderingService.IeOrderingService"
behaviorConfiguration="web"
name="DeliveryNote" >
</endpoint>
</client>
The service has a lot of methods and the one I need to test is called 'DeliveryNote'.
So lets say the service is at this address:
http://localhost:61472/eOrderingService.svc
The POST method I need to call is:
http://localhost:61472/eOrderingService.svc/DeliveryNote
And respectively the GET method is:
http://localhost:61472/eOrderingService.svc/DeliveryNote?DocumentFilter={DOCUMENTFILTER}&CustomerID={CUSTOMERID}&FromDate={FROMDATE}
The links are working but I cannot figure out how to call them from the client.
When I tested calling the POST method I received another exception:
The remote server returned an unexpected response: (400) Bad Request.
That shouldn't be true because the request I'm sending is already tested and is a valid request in XML format.
So I tried to test with a different GET method that works and receives just two DateTime parameters and not a Xml. If I try the following link:
http://localhost:61472/eOrderingService.svc/PriceChanges?startDate=2018-08-29&endDate=2018-08-30
the result is OK..
But if I call the automatically generated method "PriceChanges" the result is NULL.
I just don't get what I do wrong. It seems like a connection to the service is established but the methods are not called/build correctly. Probably because I cannot comprehend how to build the <system.serviceModel> in app.config.
I definitely should read more about the web services but I don't know where to start.
I have been assigned a old project that currently uses a WCF service.
The point is to update (more like starting fresh) the project to use ASP.NET MVC5 and Web API. The problem is that a 3rd party uses the WCF service, and they are probably not willing to make changes to their end of the communication.
The main function of the project is two basic, one is receive data, save it and just show the last status of several subjects and history graphs, and the other is to send data (turn on/off subjects).
My idea was to maintain the WCF Service (receive/send/save data) as is, just add it to new solution which consists of MVC and Web API (read data). They need (I think) to be in the same project, because the final objective is to implement SignalR on the WCF Service, if possible.
The main problem, is that the MVC and WebAPI are going to stay behind Authentication, but the WCF won't. At the moment when I try to test the WCF on the same project, it fails because it asks for a "Log in."
Error: Cannot obtain Metadata from
http://localhost:50598/ServiceTest.svc If this is a Windows (R)
Communication Foundation service to which you have access, please
check that you have enabled metadata publishing at the specified
address. For help enabling metadata publishing, please refer to the
MSDN documentation at
http://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata Exchange
Error URI: http://localhost:50598/ServiceTest.svc Metadata contains a
reference that cannot be resolved:
'http://localhost:50598/ServiceTest.svc'. The content type text/html;
charset=utf-8 of the response message does not match the content type
of the binding (application/soap+xml; charset=utf-8). If using a
custom encoder, be sure that the IsContentTypeSupported method is
implemented properly. The first 1024 bytes of the response were: '?
Log in.
Usernamehttp://localhost:50598/ServiceTest.svc The HTML document does not
contain Web service discovery information.
I tried everything that I could find on the web. But couldn't find anything that would work.
My last consisted on fiddling with the web.config:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="UnsecuredBinding">
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="Management.ServiceAspNetAjaxBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="Management.ServiceTest" behaviorConfiguration="serviceBehavior">
<endpoint address="" behaviorConfiguration="Management.ServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="ServiceLibrary.IService" bindingConfiguration="UnsecuredBinding"/>
</service>
</services>
</system.serviceModel>
I also added routes.IgnoreRoute to RouteConfig.cs
routes.IgnoreRoute("{resource}.svc/{*pathInfo}");
routes.IgnoreRoute("{resource}.svc");
and tried to add this to Global.asax
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication context = (HttpApplication)sender;
if (context.Request.Url.ToString().Contains(".svc"))
{
context.Response.SuppressFormsAuthenticationRedirect = true;
}
}
My questions:
If I migrate the WCF methods to WebAPI, will the client need to do any modifications on their end?
If yes, how can I integrate WCF on my MVC/WebAPI project, without being affected by Log In barrier.
In answer to question 1, yes your clients would have to make modifications. WCF is SOAP/XML where as WebApi is REST/JSON(usually) for starters.
As for 2, if the WCF service is working fine, just leave it as it is. You don't need to include it in the project directly, simply use the "Add Service Reference" wizard in Visual Studio to make a client for interacting with it.
As a side note, the error you where experiancing was probably due to using the inbuilt IIS express instance with Visual Studio, which doesn't support running anonymous and authenticated applications at the same time, so your WCF service ended up with authentication enabled.
Here is my approach (don't hesitate to tell me if I am doing something wrong) :
-Write XSD files defining my objects
-Use WSCF.blue to generate WSDL accordingly to the XSDs
-Use WSCF.blue to generate Web Service Code
-Implementing a stub and exposing the SVC
So far, I am not facing any problem. I can access to the .svc through my browser. But the thing is the deployed WSDL is not the same as the designed one.
When I tried to test the service with SOAP UI and the designed WSDL as source, it failed because the WSDL are differents. When I tried with the deployed one, it works fine.
Same result when I tried to generate a client (console application) with the designed WSDL (with SvcUtil.exe) : it fails the same way (ContractFilter mismatch at the EndpointDispatcher exception). it works I add a service reference.
I won't develop the client but the people they will will work on a WSDL I had to give them first. Is there a way to work with the designed WSDL or I had to give them the deployed one ?
Thanks in advance.
Excuse me for my english, I am not a native speaker.
Yes you can expose your own wsdl like this (using the example data from the wscf.blue walkthrough), with externalMetadataLocation being the important part:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="true" externalMetadataLocation="../ContractMetaData/RestaurantService.wsdl"/>
</behavior>
</serviceBehaviors>
</behaviors>
...
<services>
<service name="RestaurantService.RestaurantService" behaviorConfiguration="MyBehavior">
...
</service>
</services>
</system.serviceModel>
But I haven't had any luck making this work in conjunction with "add service reference". VS keeps generating code that isn't compatible with the webservice generated by wscf.blue.
I have a small WCF solution with 2 methods but am getting this error when I build it.
If I leave the message without dismissing it, I get
WCF Service Host cannot find any service metadata. This may cause the client application to run improperly. Please check if metadata is enabled.
I'm pretty sure my config is wrong, probably the defined endpoint does not match the namespace but I'm not sure what to set where.
The namespace of the Contracts class is JOB_1_0_Service.Contracts with 2 methods.
In the APP.Config of this project is the following:
<endpoint address="/Address1" binding="wsHttpBinding" contract="JOB_1_0_Service.Contracts.IService">
The contract methods are defined as:
[ServiceContract]
public interface IService
{
[OperationContract]
GetNearbyJobsResponse GetNearbyJobs(GetNearbyJobsRequest request);
[OperationContract]
GetChildJobsResponse GetChildJobs(GetChildJobsRequest request);
}
The namespace of the implementation class is JOB_1_0_Service.Implementation again with 2 methods:
GetNearbyJobsResponse IService.GetNearbyJobs(GetNearbyJobsRequest request)
{
...
}
and
GetChildJobsResponse IService.GetChildJobs(GetChildJobsRequest request)
{
...
}
What should I put in which config file - if indeed this is the problem?
[UPDATE]
Ok, so just to re-iterate:
I have 2 projects in 1 solution. 1 project contains the contracts and the other has the implementation code.
This also means there are 2 config files. So far I don't know which one needs modding in what way.
So, which is the one to modify, or do I need to modify both? I assume the implementation project is the one for the WCF config.
I'm now in the situation where, when I build it says I have no metadata exposed, and yet it also tries (and fails) to expose a contract as an endpoint!
[/UPDATE]
I was getting the same error becuase I had mistakenly commented out the [ServiceContract] attibute. Once I uncommented the [ServiceContract] attribute it all worked okay.
I hope this help others who face the same issue.
EDIT
Add the <serviceMetadata/> element to the service behavior for metadata
<configuration>
<system.serviceModel>
<services>
<service name="WCFTest.Service1" behaviorConfiguration="Simplebehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/WCFTest/"/>
</baseAddresses>
</host>
<endpoint
address=""
binding="basicHttpBinding"
contract="WCFTest.IService1"/>
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Simplebehavior">
<serviceMetadata/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Add below endpoint to exchange metadata
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
Aslo check this for detail : Random Error Message: WCF Service Host cannot find any service metadata
I had this error, and it turned out that I had the wrong project as the startup project: it was the first time I ever saw that error because of that, but oh well.
I just ran across this. In my case, I had three assemblies: one for the service, one for the client, and one class library shared by the first two. The shared assembly project had an app.config file automatically created by VS. Removing that file fixed the problem.
I have a simple WCF service that uses WSHttpBinding and Windows authentication. I'm trying to force the server to impersonate the client's identity upon every method call for this service.
I tried the advice given at WCF Service Impersonation, but am not exactly getting happy results. When I try to navigate to the landing page for the WCF service, I see the error:
The contract operation 'GetAdvice'
requires Windows identity for
automatic impersonation. A Windows
identity that represents the caller is
not provided by binding
('WSHttpBinding','http://tempuri.org/')
for contract
('IMagicEightBallService','http://tempuri.org/'.
Any ideas on what this error's trying to tell me?
The entire solution can be browsed at ftp://petio.org/2011/07/01/MagicEightBall/ (or downloaded at http://petio.org/2011/07/01/MagicEightBall.zip). I'm just publishing the project to a local IIS folder and accessing the service at http://localhost/MagicEightBall/MagicEightBallService.svc.
Thanks!
UPDATE:
My service's Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="Petio.MagicEightBall.MagicEightBallService" behaviorConfiguration="MagicEightBallServiceBehavior">
<endpoint name="WSHttpBinding_WindowsSecurity_IMagicEightBallService"
address="http://localhost/MagicEightBall/MagicEightBallService.svc"
binding="wsHttpBinding"
contract="Petio.MagicEightBall.IMagicEightBallService" />
<endpoint address="mex"
binding="mexHttpsBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MagicEightBallServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceAuthorization impersonateCallerForAllOperations="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
My service code:
public class MagicEightBallService : IMagicEightBallService
{
[OperationBehavior(Impersonation=ImpersonationOption.Required)]
public string GetAdvice()
{
MagicEightBall ball = new MagicEightBall();
return ball.GetAdvice();
}
}
What about minimizing the whole problem to simplest reproducible code which you can simply show here? Nobody is interested in downloading and reviewing whole your project. Moreover for later reference the related code should be still here.
I checked your just configurations of your project and your client code and I see two blocking issues:
If you want to enforce impersonation from configuration you must use only bindings with windows authentication - your endpoint exposed over HTTPS is without authentication.
Impersonation in WCF also requires client to allow service to impersonate his identity so setting the configuration on the service is not enough.
Here you have some article about impersonation and all necessary / possible settings.