I am implementing a WCF Web Service responsible of publishing data via REST and SOAP by using multiple bindings. The service is to be hosted on an IIS.
I have previously written some WCF services and know a bit about configuring those by using the web.config and setting up routes in the Global.asax files, however I'm confused about how to make the most "clean" configuration or the best practice on configuring a WCF service.
Here's what I have figured so far:
The web.config can be used to setup bindings, endpoints, security etc - is this needed when hosting the service on IIS or can the configuration be done on the IIS?
By using the Global.asax we're able to configure routings (and lots of other stuff). but is it the right place to do this?
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("Service", new WebServiceHostFactory(), typeof(Service)));
}
I've spent some time googling this topic and it seems that every link has it's own opinion on how to accomplish the task.
Therefore I would like some input on how to configure/implement a WCF service to support the following:
Publish the data via REST/JSON and
Pubish the data via SOAP/XML and publishing metadata
Provide to different services; http://domain.com/Service and http://domain.com/AuthService
For the record I'm aware of how to publish the data using both SOAP/REST - that's not the problem. I'm just trying to make to most clean/minimal configuration for the service.
Any feedback is greatly appreciated.
Here is how I've done this.
Web.config:
<system.serviceModel>
<services>
<service name="Service">
<endpoint address="soap" contract="IService" binding="basicHttpBinding"/>
<endpoint address="rest" contract="IService" binding="webHttpBinding" behaviorConfiguration="restBehavior"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="restBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
The contract looks like this:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(UriTemplate="/Update/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
void Update(string id, Entity entity);
}
With .net 4.5 you can omit any configuration in .config file at all. [ServiceContract], [OperationContract], [DataContract] would be essential. They do not say that in documentation explicitly, but it works :)
".NET Framework 4.5 makes configuring a WCF service easier by removing the requirement for the service element. If you do not add a service section or add any endpoints in a service section and your service does not programmatically define any endpoints, then a set of default endpoints are automatically added to your service, one for each service base address and for each contract implemented by your service." - from
https://msdn.microsoft.com/en-us/library/ee358768%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
Related
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.
Well. I have my client layer which has
Client Contracts project which is class library contains my Service Contracts
Client Proxies which is class library and implement the Client Contracts
Client Entities which is class library to be used in client contracts and client proxies
Client Bootstrapper which is class library to be used for MEF loader purpose
and I have my web mvc project which controllers are api to call my service
and I add refrence for all lient layers in my web project
then configure my service in the web.config file as
<system.serviceModel>
<client>
<endpoint address="http://localhost:3167/CountryClient"
binding="basicHttpBinding"
contract="Client.Contracts.ICountryService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange" />
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
and my client proxy class
namespace Client.Proxies
{
[Export(typeof(ICountryService))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class CountryClient : ClientBase<ICountryService>, ICountryService
{
public Entities.Country[] GetAllCountriess(int? Take, int? Skip)
{
return Channel.GetAllCountriess(Take, Skip);
}
}
}
and my Contract as following :
namespace Client.Contracts
{
[ServiceContract]
public interface ICountryService : IServiceContract
{
[OperationContract]
Country[] GetAllCountriess(int? Take, int? Skip);
}
}
when running my web using visual studio : Error happened once i'm in return channel
Error saying that There was no endpoint listening at http://localhost:3167/CountryClient that could accept the message
and the inner exception says: The remote server returned an error: (404) Not Found
i'm running visual studio as administrator
I stopped the firewall
the port is not used
not antivirus or firewall is preventing from connection
my Unit test is working with same configuration
Do you have your service project running in debug when your running your client project test? The url: http://localhost:3167/CountryClient has to be valid service endpoint. If you have Visual Studio solution with both Service and Client project running then make sure both are executing and enabled. If your service project is a separate project, then make sure you are running that as well.
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.
IIS 7.0 on Windows 2008
WCF Web Service, .NET 4 from VS 2010
Web service is installed via publishing and I have full admin rights on the server. There are several complicated methods, but there is a simple one that returns the build version. If we can get this one working, I can fix them all - here is my interface:
namespace MyNameSpace
{
[ServiceContract]
public interface WebInterface
{
[OperationContract]
[WebGet]
string GetVersion();
Attempt to connect via HTTP:// and everything works fine!
Attempt to conenct via HTTPS:// I get a 404 file not found.
I can reach the generic "You have created a web service..." page, including full web service path and the C# generic sample code when browsing to the exact same URL's both on HTTP and HTTPS.
In C#, I have read that the certificate can cause trouble, and I have already implemented the delegate overload to approve our server certificate.
I suspect missing one or more entries in the Web.config file, but I don't have a clue where to start. I have tried Google searching and Stack Overflow searching, but I haven't found the correct combination of search terms to help with this particular issue.
Web Config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="HttpGetMetadata">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="LinkService" behaviorConfiguration="HttpGetMetadata">
<endpoint address="" contract="WebInterface" binding="basicHttpBinding" />
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Help Please.
You're using the defaults for basicHttpBinding, and the default security mode for that binding is None. You need to define the binding and set the security mode to Transport in your config. Add a Bindings section to your ServiceModel section, like this:
<serviceModel>
<Bindings>
<basicHttpBinding name="secureBinding">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</basicHttpBinding>
</Bindings>
</serviceModel>
Then you need to assign this binding to your endpoint via the bindingConfiguration attribute, like this:
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="secureBinding"
contract="WebInterface" />
You'll probably want to enable httpsGetEnabled as well:
<serviceMetadata httpGetEnabled="true"
httpsGetEnabled="true" />
See BasicBinding with Transport Security (which is what the sample code is based on).
You can also google with terms like "BasicHttpBinding WCF SSL" and stuff like that - lots of examples and information on the web, it's just a matter of using the right words :)
Also, I'm not 100% confident that the transportClientCredential setting is correct for your scenario (it might need to be Certificate), but I've done very little with SSL for WCF.
There may be other issues as well (like how IIS is set up on your machine), but the above is what's needed for the config.
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.