Expand your WCF service with REST

Posted on November 19, 2017 in Develop
Updated: December 16, 2017

If you are working with .NET you probably have WCF services.
You might have thought of expanding those WCF services to REST allowing you to make mobile or SPA frontends.
Instead of using ServiceStack or WebApi+Swashbuckle, you can stay in WCF. This have the advantage of a smaller learning curve for other developers.

After this walk-through you have a REST service and swagger.yaml that you can use for creating client code via https://editor.swagger.io/

IBookService.yaml

You can find code for this guide on github.

HowTo create a REST service (including Swagger.yaml) from a WCF service

If you have an existing service you create yet an endpoint allowing you to keep the existing service.

In most cases the best thing is to create yet a WCF service using the same contract as your original WCF service. Just start from bullet 2 then.

1. The templates

This guide is based on Visusal Studio 2017.4

  • Create an empty web
    • File - New - Project - ASP.NET Web Application - Empty
    • => This gives you a project without Global.Ajax and startup files
  • Add Ajax-enabled WCF service
    • Solution - RightClick project - Add - New Item (Ctrl-Shft-A) - WCF Service (Ajax-enabled) - You could call it RestService1
    • => This gives you a WCF service with webHttpBinding and a ref to System.ServiceModel.Web

2. The first response

  • Change from SOAP to REST response
    • In RestService1.svc.cs add below [OperationContract]:
    [WebGet(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

3. Change response to <empty> for void functions

4. Add Sample Interface

    public class RestService1 : IBookService
    • Click on LigthBulp - Implement Interface
  • Change contract. In web.config change from <service contract="WebApplicationWcfRest1.RestService1
    to <service contract="WebApplicationWcfRest1.interfaces.IBookService
  • Implement GetBookById(). Add line:
    return new Book() {Id = 1, Name= "The incredible stamp" };

5. Create Swagger Yaml

  • Create Swagger.yaml - this is the wsdl for REST
    • In Project Properties (Alt-Enter) - Build - Select XML Documentation file - Clear the path
    • Install https://www.nuget.org/packages/Swagger4WCF into the project containing the interfaces
    • Build project
    • => The yaml file is in \bin\WebApplicationWcfRest1.IBookService.yaml
  • Edit yaml file
    • Replace host from localhost to localhost:15563 (or to the test- or prod server host)
    • Replace basePath from /IBookService to /RestService1.svc
    • Replace all paths from e.g. /GetBooksList: to /Book: (as you wrote in UriTemplate)
    • Group operations with same path together and delete the duplicate paths
    • Those paths having path parameters e.g. /{id} change parameters from in: query to in: path
    • Save the yaml file into \interfaces\ - update version number each time you send a new version to the client
  • Test the yaml file
    • Goto http://editor.swagger.io/
    • Replace left pane with the content of the yaml file (if you use chrome, you can paste)
    • => In top of right pane: The should be no errors

6. Move contracts to new library

  • Swagger4WCF does not work well with Unity.WCF, so we move the contracts to a new library
    • File - New - Project (Ctrl-Shft-N) - Class Library - Name: Contracts
    • Drag'n'drop folder interfaces to Contracts
    • Drag'n'drop folder models to Contracts
    • Add Refs to project Contracts:
      • System.ServiceModel
      • System.ServiceModel.Web
      • System.Runtime.Serialization
  • Create Swagger.yaml - this is the wsdl for REST
    • In Project Properties (Alt-Enter) - Build - Selt "XML Documentation file" - Clear the path
    • Install https://www.nuget.org/packages/Swagger4WCF into the project containing the interfaces (Contracts)
    • Build project
    • => The yaml file is in \bin\WebApplicationWcfRest1.IBookService.yaml
  • Remove Swagger4WCF from project WebApplicationWcfRest1
    • In project WebApplicationWcfRest1 add ref to project Contracts
    • In packages.config remove line having Swagger4WCF
    • Rebuild Solution

7. Add dependency injection with Unity

  • Remove Swagger4WCF from service project
    • Unload project WebApplicationWcfRest1
    • Remove two lines containing Swagger4WCF near bottom
  • Add dependency injection
    • Install https://www.nuget.org/packages/Unity.Wcf into the project containing the services (WebApplicationWcfRest1)
    • => This created file WcfServiceFactory.cs
    • View Markup of RestService1.svc
    • Replace: CodeBehind="RestService1.svc.cs"
      • with: Factory="WebApplicationWcfRest1.WcfServiceFactory"
    • In WcfServiceFactory.cs register the service:
               .RegisterType<IBookService, RestService1>();

8. Use HTTP Status Codes

  • Add HTTP Status Codes to your service
    • In your service RestService1.svc.cs - method AddBook() add content
            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Created; // 201
            if (book.Name == "The incredible stamp") {   // Book exist
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Conflict; // 409
            }
    • In method UpdateBook() add content
            if (book.Id == 2) { // Book does not exist - 404
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Resource not found");
            } else if (book.Name == "") { // Invalid request
                WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.MethodNotAllowed; // 405
            }
    • In method GetBookById() add content
            if (id == "2") { // Book does not exist - 404
                WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Resource not found");
                return null;
            } else {
                return new Book() { Id = 1, Name = "The incredible stamp" };
            }
    put:
      responses:
        201:
          description: "Book created"
        409:
          description: "Book exist"
    post:
      responses:
        404:
          description: "Book not found"
        405:
          description: "Validation exception"
    get:
      responses:
        404:
          description: "Book not found"

9. CORS

To be able to call the API you need to allow clients to call it. You can do that already in global.asax

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost");
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}

10. Security

Towards a SPA or mobile you could use OAuth as demonstrated in Secure WCF RESTful service using OAUTH
Towards a partner you could use certificates as demonstrated in Secure a WCF REST Service with an X509 Certificate, hosted on IIS

11. Adding Swagger examples

To make your API clearer to understand you can add Swagger examples

Refs / Credits

The End