0

I have a WCF Rest service and while calling the service methods, we need to validate the license key which is as part of the every request Message (request and response message will be JSON Format). To validate the license key, we need to call the separate method for every request and this method will contains the logic like, if input request contains invalid license key, then we need to send the error message like "Invalid license key" in the JSON format to the client. For this, Do we have any approach in WCF Rest to call this method for every request automatically instead of calling explicitly for each request?

Note :- The license key will be available in the Service Web.Config file.

vellaichamy
  • 143
  • 1
  • 10

1 Answers1

2

You can use a message inspector, which is called for every request coming to the service. The code below shows one implementation of such an inspector.

public class StackOverflow_25380450
{
    [ServiceContract]
    public class Service
    {
        [WebGet]
        public int Add(int x, int y)
        {
            return x + y;
        }
    }

    public class MyInspector : IDispatchMessageInspector, IEndpointBehavior
    {
        public const string LicenseHeaderName = "X-License";
        public const string ExpectedLicense = "abcdef";

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            HttpRequestMessageProperty reqProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
            var license = reqProp.Headers[LicenseHeaderName];
            if (license != ExpectedLicense)
            {
                throw new WebFaultException<string>("License required", HttpStatusCode.Forbidden);
            }

            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }

    public static string SendGet(string uri, Dictionary<string, string> headers)
    {
        string responseBody = null;

        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
        req.Method = "GET";
        if (headers != null)
        {
            foreach (string headerName in headers.Keys)
            {
                switch (headerName)
                {
                    case "Accept":
                        req.Accept = headers[headerName];
                        break;
                    default:
                        req.Headers[headerName] = headers[headerName];
                        break;
                }
            }
        }

        HttpWebResponse resp;
        try
        {
            resp = (HttpWebResponse)req.GetResponse();
        }
        catch (WebException e)
        {
            resp = (HttpWebResponse)e.Response;
        }

        if (resp == null)
        {
            responseBody = null;
            Console.WriteLine("Response is null");
        }
        else
        {
            Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
            foreach (string headerName in resp.Headers.AllKeys)
            {
                Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
            }
            Console.WriteLine();
            Stream respStream = resp.GetResponseStream();
            if (respStream != null)
            {
                responseBody = new StreamReader(respStream).ReadToEnd();
                Console.WriteLine(responseBody);
            }
            else
            {
                Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
            }
        }

        Console.WriteLine();
        Console.WriteLine("  *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*  ");
        Console.WriteLine();

        return responseBody;
    }

    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        var endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
        endpoint.Behaviors.Add(new WebHttpBehavior { AutomaticFormatSelectionEnabled = true });
        endpoint.Behaviors.Add(new MyInspector());
        host.Open();
        Console.WriteLine("Host opened");

        Console.WriteLine("No license:");
        Dictionary<string, string> headers = new Dictionary<string, string>
        {
            { "Accept", "application/json" }
        };
        SendGet(baseAddress + "/Add?x=6&y=8", headers);

        Console.WriteLine("Incorrect license:");
        headers.Add(MyInspector.LicenseHeaderName, "incorrect");
        SendGet(baseAddress + "/Add?x=6&y=8", headers);

        headers[MyInspector.LicenseHeaderName] = MyInspector.ExpectedLicense;
        SendGet(baseAddress + "/Add?x=6&y=8", headers);

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

You can find more information about inspectors in general at http://blogs.msdn.com/b/carlosfigueira/archive/2011/04/19/wcf-extensibility-message-inspectors.aspx.

carlosfigueira
  • 85,035
  • 14
  • 131
  • 171
  • This is what i was expected. thanks for sharing this. keep your code as a base, i have refered [this blog](http://trycatch.me/adding-custom-message-headers-to-a-wcf-service-using-inspectors-behaviors/). It is very useful. Finally i am happy to get executable code. – vellaichamy Aug 20 '14 at 10:29
  • need a one small clarification, how to retrieve the license key from the Body instead of header? – vellaichamy Aug 20 '14 at 10:39
  • If the key is in the body, then you'll need to read the `request` parameter passed to the `AfterReceiveRequest` method. Notice, though, that the `Message` object can only be read once, so after reading it you'll need to create a new `Message` instance (the reason why the parameter is passed by reference) so that the message can be read again by the rest of the WCF pipeline. – carlosfigueira Aug 20 '14 at 12:58