tags:

views:

41

answers:

2

I Have a Custom WCF Service Host (webServicehost2) and Factory that is doing some dependency injection (implementing a custom IInstanceProvider) and also some custom authentication (impementing a custom RequestInterceptor).

I Have a very small issue in that when i navigate to a REST resource that does not exsist. for example

http://localhost/restservice.svc/
http://localhost/restservice.svc/blah/

I get a 404 Error, OK that is expected.

What I would like to know is there anyway that I can capture the returning Http error and format it a little nicer.

+1  A: 

I'm not sure if there's an easier way, but I did some quick prototyping here, and found that one way to accomplish this is by adding a custom IDispatchMessageInspector implementation that looks at the response message of the service and replaces the content with your own custom HTML page.

It goes basically like this:

  • Implement IDispatchMessageInspector.BeforeSendReply()
  • Look at the reply message, and grab the httpResponse property, and check if it is an error based on the StatusCode property.
  • If it's an error, create a brand new WCF message with the body set to the HTML content you want to serve.
  • Add the httpResponse and webBodyFormatMessageProperty properties to the new message again and set that as the reply.

I have my sample that does this successfully, but it's way ugly; I'll need to clean it up a bit before posting it.

tomasr
Hey this worked great!
Bluephlame
A: 

Here are the relevant code snipets for anyone else attemping this

First an instance of the IDispatchMessageInspector

 public class CustomResponseFormatterMessageInspector : IDispatchMessageInspector
    {
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            var prop = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
            if (prop.StatusCode == HttpStatusCode.NotFound)
            {
                ErrorResponse(ref reply);
            }
        }

        private void ErrorResponse(ref Message original)
        {
            const string ERROR_HTML = @"<html><HEAD><TITLE>Request Error</TITLE></HEAD><BODY> <H1>My Error processing request {1}</H1><P>{0}</P></BODY></html>";

            XElement response = XElement.Load(new StringReader(string.Format(ERROR_HTML, "A Resource does not exsist at this location.", HttpStatusCode.NotFound)));
            Message reply = Message.CreateMessage(original.Version, null, response);
            reply.Headers.CopyHeadersFrom(original);
            reply.Properties.CopyProperties(original.Properties);
            original = reply;
        }
    }

Then to inject this into the IServiceBehaviour I added

ed.DispatchRuntime.MessageInspectors.Add(new CustomResponseFormatterMessageInspector());

There may be other code in this that is relevant to my implementation but that is all i added.

 public class DependencyInjectionServiceBehavior : IServiceBehavior
    {
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
            {
                var cd = cdb as ChannelDispatcher;
                if (cd != null)
                {
                    foreach (EndpointDispatcher ed in cd.Endpoints)
                    {
                        ed.DispatchRuntime.InstanceProvider =
                            new DependencyInjectionInstanceProvider(serviceDescription.ServiceType);
                        ed.DispatchRuntime.MessageInspectors.Add(new CustomResponseFormatterMessageInspector());
                    }
                }
            }
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
    }
Bluephlame