Using Swagger with WCF REST - wcf

I have a WCF REST based service that I would like to add Swagger to. I have installed Swagger.NET package, my project is using .NET 4.0. I have also enabled XML documentation, etc... but now I am not sure the best route from here.
When i launch http://localhost/SwaggerUI/index.html I get the default page with the http://YOUR-URL-HERE:PORT/api/swagger. I am not sure what url should go here. I did enable help of the service via: <standardEndpoint name="" helpEnabled="true" /> which does give me the ugly page here: http://localhost/api/help -- when i plug that into Swagger i just get the result:
200 : OK http://localhost/api/help
What is the best way to proceed, i am not using WebApi but have many of the features available if that helps.

As of December 2015, there is an implementation of Swagger for Wcf now. You should check this out - Swagger for WCF - SwaggerWcf

Currently there are no implementations of Swagger for WCF. You will either need to implement your own by following the spec from https://github.com/wordnik/swagger-core/wiki, or wait patiently for someone to implement one for you.
I currently have an implementation in progress, but it's not quite ready for primetime. The basic spec is deceptively simple, but fitting it to WCF is a definite challenge.

I tried to implement swagger for WCF in my application as the implementation is different as we implanted for web API(simpler). I will tell you how its different and how it works step by step:-
**1. Why Swagger4Wcf**
----------------------
•Manually writing yaml description for swagger and maintain it especially WCF services are boring.
•There is a nuget package called Swagger4WCF that automatically generates yaml description for swagger 2.0 for each interface matching attributes used by WCF (ServiceContract/OperationContract/WebGet/WebInvoke).
2. How Swagger Works in the Background
--------------------------------------
Swagger4WCF uses NuPack post build pattern to trigger at build time.
https://www.codeproject.com/Tips/1190360/How-to-setup-a-managed-postbuild-without-scripting
3.At build time, it will detect assemblies present in output directory, open them with mono.cecil (to reflect assemblies) to generate expected yaml description for swagger 2.0.
Swagger4WCF detects **WebGet/WebInvoke** to provide Verb/Method in serialization style in yaml.
Steps to implement Swagger in your application:-
1. Install SwaggerWcf package
2. Configure WCF routes
We have to add the route in the Application_Start method inside Global.asax
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("v1/rest", new WebServiceHostFactory(), typeof(BookStore)));
RouteTable.Routes.Add(new ServiceRoute("api-docs", new WebServiceHostFactory(), typeof(SwaggerWcfEndpoint)));
}
Note: Edit Web.config and add the following (if it doesn't exist yet) inside the system.serviceModel block
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
3. Configure WCF response auto types (optional)
We have to add the following to Web.config. This will allow the WCF service to accept requests and send replies based on the Content-Type headers.
<behavior name="webHttpBehavior">
<webHttp defaultOutgoingResponseFormat="Json" automaticFormatSelectionEnabled="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
4. Decorate WCF services interfaces
For each method, we have to configure the WebInvoke or WebGet attribute, and add a SwaggerWcfPath attribute.
[SwaggerWcfPath("Get book", "Retrieve a book from the store using its id")]
[WebGet(UriTemplate = "/books/{id}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Book ReadBook(string id);
5. Decorate WCF services class
• Add the SwaggerWcf and AspNetCompatibilityRequirements attributes to the class providing the base path for the service.
• For each method, add the SwaggerWcfTag to categorize the method and theSwaggerWcfResponse for each possible response from the service.
[SwaggerWcfTag("Books")]
[SwaggerWcfResponse(HttpStatusCode.OK, "Book found, value in the response body")]
[SwaggerWcfResponse(HttpStatusCode.NoContent, "No books", true)]
public Book[] ReadBooks()
{
}
6. Decorate data types used in WCF services
[DataContract]
[Description("Book with title, first publish date, author and language")]
[SwaggerWcfDefinition(ExternalDocsUrl = "http://en.wikipedia.org/wiki/Book", ExternalDocsDescription = "Description of a book")]
public class Book
{
[DataMember]
[Description("Book ID")]
public string Id { get; set; }
[DataMember]
[Description("Book Title")]
public string Title { get; set; }
[DataMember]
[Description("Book First Publish Date")]
public int FirstPublished { get; set; }
[DataMember]
[Description("Book Author")]
public Author Author { get; set; }
[DataMember]
[Description("Book Language")]
public Language Language { get; set; }
}
That's it wcf for Swagger implemented.
Please free if you face any issue.
Thanks,
Abhi

Related

Can we use ChannelFactory<T> when the contracts (.NET classes, interfaces) are NOT defined in some common library consumed by client and service?

I was exploring ChannelFactory and while doing so I did following:
A service contract in assembly named "Common":
namespace Common
{
using System.ServiceModel;
[ServiceContract(Name = "ITestService", Namespace = "http://test/")]
public interface ITestService
{
[OperationContract(Name = "SayHello")]
string SayHello(string request);
}
}
A service hosted under web application called "WcfServiceApp":
Note that I have created another service interface (contract) to create a service. But the names of contracts and the namespaces are same as the contract defined in the "Common" assembly.
namespace WcfServiceApp
{
[ServiceContract(Name = "ITestService", Namespace = "http://test/")]
public interface ITestServiceWithDiffDotNetName
{
[OperationContract(Name = "SayHello")]
string SayHelloAgain(string name);
}
// This service implements new service contract.
public class TestService : ITestServiceWithDiffDotNetName
{
public string SayHelloAgain(string request)
{
return "hello " + request;
}
}
// This service implements common service contract
public class TestService2 : Common.ITestService
{
public string SayHello(string request)
{
return "hello " + request;
}
}
}
There are two ".svc" files (TestService.svc and TestService2.svc), each for services created above. Web.config has following:
<system.serviceModel>
<services>
<service name ="WcfServiceApp.TestService" >
<endpoint binding="basicHttpBinding" contract="WcfServiceApp.ITestServiceWithDiffDotNetName"></endpoint>
</service>
<service name ="WcfServiceApp.TestService2" >
<endpoint binding="basicHttpBinding" contract="Common.ITestService"></endpoint>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
A client which calls these two services:
Note that client is using ChannelFactory and the service contract defined in "Common" library.
ChannelFactory<ITestService> commonServiceChannel = new ChannelFactory<ITestService>(new BasicHttpBinding(), "http://localhost/WcfServiceApp/TestService.svc");
var proxy = commonServiceChannel.CreateChannel();
var response = proxy.SayHello("Mike"); // response = "Hello"
ChannelFactory<ITestService> commonServiceChannel2 = new ChannelFactory<ITestService>(new BasicHttpBinding(), "http://localhost/WcfServiceApp/TestService2.svc");
var proxy2 = commonServiceChannel2.CreateChannel();
var response2 = proxy2.SayHello("Mike"); // response2 = "Hello Mike"
Question:
I observed that first service (created using some different service contract) receives the null argument whereas argument received in second service (created using service contract used to create ChannelFactory) is "Mike" as expected.
In Fiddler, I can see that request parameter correctly.
Why does this happen?
If all XML names and namespaces are same (although names of .NET interfaces are different), should the service call not succeed as underlying SOAP messages would be same?
I am afraid what will happen if my customer's applications want to create service in Java and my application is supposed to call it?
Try this, get the WSDL document from each version of your service. Compare the WSDL documents (.NET 4.5 supports single file WSDL documents out of the box) to see what WCF is expecting in the soap message for each service. Chances are a default XML namespace was taken from the .NET (different) namespaces somewhere thus making the "identical" service contracts actually be different. WCF does a lot for you in naming the XML namespaces and you will likely need to manually override those defaults throughout the service, operation and data contracts to make both services support identical soap messages.
On integrating with Java, as long as the Java client can be generated from the WSDL the service outputs there's a chance there won't be any issues. The big exception is configuring the security and authentication aspects of the Java client. This good blog post on specific WCF bindings for Java interop would be worth a look when working with Java based clients.
Thanks to Sixto Saez first.
I compared the WSDL files generated (using svcutil.exe) by both the services and found that they were NOT EXACTLY SAME. However, it was not due to any conflict in the name or namespace of either ServiceContract or OperationContract itself!
It was due to difference in the parameter names used in the definition of the OperationContracts!
You can see that OperationContract "SayHello" has a parameter named "request". On the other hand, OperationContract named "SayHelloAgain" has a parameter named "name". When I changed the name of the parameter from "request" to "name" as it is in second OperationContract, it worked!
So the conclusion is:
ChannelFactory WORKS when the contracts (.NET classes, interfaces) are NOT defined in some common library consumed by client and service. Only thing is that WSDLs generated by those service contracts have to match with each other.

Using Castle Windsor WcfFacility to create client endpoints

I have created three assemblies. A web site, a WCF service and a contracts assembly that holds the interfaces that the services implement. I would like to use Castle Windsor to create the services for me on the client (website) so that I do not have to have an endpoint in the web.config of the web site for each service that I wish to use.
I would like to look at the contract assembly and get all the service interfaces in a namespace. Right now for every service I have something like the following when registering the components with the container.
container.Register(Component.For<ChannelFactory<IMyService>>().DependsOn(new { endpointConfigurationName = "MyServiceEndpoint" }).LifeStyle.Singleton);
container.Register(Component.For<IMyService>().UsingFactoryMethod((kernel, creationContext) => kernel.Resolve<ChannelFactory<IMyService>>().CreateChannel()).LifeStyle.PerWebRequest);
and in my web.config I have the setup code.
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="AuthToken" type="MyNamespace.Infrastructure.AuthTokenBehavior, MyNamespace.Contracts" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior>
<AuthToken />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"></readerQuotas>
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint name="MyServiceEndpoint" address="http://someurl/MyService.svc" binding="wsHttpBinding" contract="MyNamespace.Contracts.IMyService"></endpoint>
</client>
</system.serviceModel>
I end up with multiple service endpoints that all look almost exactly the same and when we deploy onto clients machines they have to set the address of every endpoint even though the base url is the same for every one.
I would like to have a base url in my web.config that is grabbed through code and then have the services registered with the container using reflection on the contracts assembly. I do need the specialised endpoint behaviour that is in the above config file.
Where so I start? the WcfFacility looks great but the doco is a bit lacking...
I agree the docs for the wcf facility are lacking and that is sad because it is a really great tool and it would be a real shame if people didn't use it because they could not get started, so let me see if I can help you out a little bit if I can...
Let's create a three project application that has:
A class library for shared contracts
A console application that acts as a server
A console application that acts as a client
The idea is that we want to be able to use the service names when we register the services and to share a base URL (I think that is what you were asking and if not, hopefully you can extrapolate from here). So, firstly, the shared contracts simply has this in it (nothing special, normal WCF fare):
[ServiceContract]
public interface IMyService1
{
[OperationContract]
void DoSomething();
}
[ServiceContract]
public interface IMyService2
{
[OperationContract]
void DoSomethingToo();
}
Now the server console application looks like this, we firstly implement the service contracts (again nothing special there, just classes implementing interfaces) and then just register them all as services (notice no need for any configuration file here and you can change the way you decide what are services etc using all the options that Windsor gives you - my scheme is a bit limited but it gives you an idea):
namespace Services
{
public class MyService1 : IMyService1
{
public void DoSomething()
{
}
}
public class MyService2 : IMyService2
{
public void DoSomethingToo()
{
}
}
}
//... In some other namespace...
class Program
{
// Console application main
static void Main()
{
// Construct the container, add the facility and then register all
// the types in the same namespace as the MyService1 implementation
// as WCF services using the name as the URL (so for example
// MyService1 would be http://localhost/MyServices/MyService1) and
// with the default interface as teh service contract
var container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
AllTypes
.FromThisAssembly()
.InSameNamespaceAs<MyService1>()
.WithServiceDefaultInterfaces()
.Configure(c =>
c.Named(c.Implementation.Name)
.AsWcfService(
new DefaultServiceModel()
.AddEndpoints(WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Implementation.Name)
)))));
// Now just wait for a Q before shutting down
while (Console.ReadKey().Key != ConsoleKey.Q)
{
}
}
}
And that is the server, now how to consume these services? Well, actually that is quite easy, here is a client console application (it references just the contracts class library):
class Program
{
static void Main()
{
// Create the container, add the facilty and then use all the
// interfaces in the same namespace as IMyService1 in the assembly
// that contains the aforementioned namesapce as WCF client proxies
IWindsorContainer container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
Types
.FromAssemblyContaining<IMyService1>()
.InSameNamespaceAs<IMyService1>()
.Configure(
c => c.Named(c.Implementation.Name)
.AsWcfClient(new DefaultClientModel
{
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Name.Substring(1)))
})));
// Now we just resolve them from the container and call an operation
// to test it - of course, now they are in the container you can get
// hold of them just like any other Castle registered component
var service1 = container.Resolve<IMyService1>();
service1.DoSomething();
var service2 = container.Resolve<IMyService2>();
service2.DoSomethingToo();
}
}
That's it - hopefully this will get you started (I find that experimenting and using the intellisense usually gets me where I need to go). I showed you both the service and client sides but you can just use one or the other if you prefer.
You should be able to see where the binding is configured and how I have gone about constructing the URLs so in your case you could easily just pluck your base URL from a configuration file or whatever you want to do.
One last thing to mention is that you can add your custom endpoint behaviour by adding it as an extension to the endpoint, so in the client example you would have something like this:
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format("http://localhost/MyServices/{0}", c.Name.Substring(1)))
.AddExtensions(new AuthTokenBehavior())

WCF 400 Bad Request

I created a simple function
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json)]
string Start();
Definition,
public String Start()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize("Check");
}
From browser using Javascript/Jquery,
http://localhost/service1.svc tells me I have created a service and all other info.. Looks fine.
I'm trying to call this using
http://localhost/service1.svc/Start
I get a 400 bad request for this call. I hope I'm not doing something totally wrong here. I should be able to access WCF service from browser right?
I tried looking a lot before I thought of posting. But I'm unable to get this basic thing working is frustrating me.
EDIT & UPDATE
Now I'm at this stage. The service page is telling me that the metadata service is disabled and is asking me to insert the following text
<serviceMetadata httpGetEnabled="true" />
I inserted the text - but still it shows the same text!! This is getting too confusing now..
Try to change POST with GET and restart the request
Works for me. I created WCF Rest Service.
I use URL which looks like http://localhost:8080/Service1/Start
Here is the code:
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web.Script.Serialization;
namespace WcfRestService1
{
// Start the service and browse to http://<machine_name>:<port>/Service1/help to view the service's generated help page
// NOTE: By default, a new instance of the service is created for each call; change the InstanceContextMode to Single if you want
// a single instance of the service to process all calls.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
// NOTE: If the service is renamed, remember to update the global.asax.cs file
public class Service1
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json)]
public string Start()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize("Check");
}
}
}

WCF 4.0 : WebMessageFormat.Json not working with WCF REST Template

Downloaded the WCF REST Template from this location.
The default response format is XML, which works great. However, when I try to get a JSON response, I still get XML.
This is my modified code -
[WebGet(UriTemplate = "",ResponseFormat = WebMessageFormat.Json)]
public List<SampleItem> GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } };
}
Note the ResponseFormat=WebMessageFormat.Json. That is the only change I did to that template.
What am I missing?
Thanks!
Figured out. automaticFormatSelectionEnabled property for standardendpoint should be set to false and defaultOutgoingReponseFormat should be set to Json.
<standardEndpoint name="" helpEnabled="true"
automaticFormatSelectionEnabled="false"
defaultOutgoingResponseFormat ="Json" />
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat="Json"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
Changes to 2 attributes within the web.config will fix it:
automaticFormatSelectionEnabled=false
defaultOutgoingResponseFormat=Json (edited: from "true")
For me, setting the response format to JSON in the WebGet attribute doesn't work. Setting it in the body of the method does;
// This works
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
return jsonData;
// This doesn't work
`[WebGet(UriTemplate = "/conditions?term={term}", ResponseFormat = WebMessageFormat.Json)]`
Click -> reference links
"When automatic format selection is enabled, the infrastructure parses the Accept header of the request message and determines the most appropriate response format. If the Accept header does not specify a suitable response format, the infrastructure uses the Content-Type of the request message or the default response format of the operation."
EDIT: this link might get you moving ahead
http://blogs.msdn.com/b/endpoint/archive/2010/11/01/wcf-webhttp-service-returns-http-415-unsupported-media-type.aspx
I was hitting issues like this every time I tried to create a JSON web service.
Now, I just follow the steps shown here.
http://mikesknowledgebase.com/pages/Services/WebServices-Page1.htm
It shows how to create a JSON web service, step-by-step, with screenshots and examples.
Hope this helps.

WCF custom MessageEncoder requires custom Behavior

I have written a custom MessageEncoder for a WCF pipeline, which is then applied using a BindingElementExtensionElement within my app.config.
On the server, when a message arrives, my message encoder needs to pull some information from the stream, and hold it for the duration of the Operation Context, so that it may be returned as a header in the response. The header manipulation screams of Behavior functionality, so...
I would like to know, how do I get my custom encoder to also apply a custom Behavior to the pipeline (which is where I will take care of storing this special 'information' across an operation context) - the encoder is essentially useless without the behavior, so I dont want to rely on the user remembering to add the behavior config when they add the encoder config - it should just happen automatically under the covers.
Additionally, where is a good resource for reading about the lifecycle of these pipeline elements?
Thanks
I wrote custom headers and message inspectors for WCF and found this article and some of the links in it useful along my way:
Handling custom SOAP headers via WCF Behaviors
HTH
You have basically two options:
1) either you expose your behavior via an attribute, so that it can be added to the server's config file - something like this:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<YourCustomMessageEncoderBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
2) or you can create your own custom ServiceHost which makes sure to add this behavior to the list of dispatch behaviors if it's not already there.
class YourCustomServiceHost : ServiceHost
{
public YourCustomServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
YourCustomMessageEncodingBehavior behavior =
this.Description.Behaviors.Find<YourCustomMessageEncodingBehavior>();
if (behavior == null)
{
behavior = new YourCustomMessageEncodingBehavior();
this.Description.Behaviors.Add(behavior);
}
else
{
return;
}
}
}
Marc