Handling Invalid URI passed to a WCF service

2019-03-03 08:45发布

I have WebGet, and WebInvoke attributes describing my contract, but what is the best method of handling invalid URI's? Right now, if a user passes an URI that does not match my current operations, they get an "Endpoint not found." message. I want to pass back a more descriptive message.

For example, my URI template looks like:

/Stuff/{ID}/subStuff

but say they type

/Stuff/{ID}/OtherStuff

There is no such thing as OtherStuff, and I do not have a template for that.

Is there a way to cover all non mapped URI's with a single contract?

Thanks!

标签: wcf rest
2条回答
唯我独甜
2楼-- · 2019-03-03 09:09

While I did follow the links mark provided, and they did give a hint of what I needed. The answers that were linked did not actually answer my original question.

I was able to follow the steps, and I wanted to list my steps to solve this problem on this question as well.

To create my own response to any URI that was not mapped to a method in my contract I created the following:

  • A custom ServiceHostFactory
  • Behavior that I mapped to my end points within the custom ServiceHostFactory
  • a dispatcher that would handle all unmapped uri's that were provided to the service.

Below are the full definitions of the object's I created:

using System.ServiceModel;
using System.ServiceModel.Activation;

namespace your.namespace.here
{
    public class CustomServiceHostFactory : WebServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
            //note: these endpoints will not exist yet, if you are relying on the svc system to generate your endpoints for you
            // calling host.AddDefaultEndpoints provides you the endpoints you need to add the behavior we need.
            var endpoints = host.AddDefaultEndpoints();
            foreach (var endpoint in endpoints)
            {
                endpoint.Behaviors.Add(new WcfUnkownUriBehavior());
            }

            return host;
        }
    }
}

As you can see above, we are adding a new behavior: WcfUnknownUriBehavior. This new custom behavior's soul duty is to replace the UnknownDispatcher. below is that implementation:

using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
namespace your.namespace.here
{
    public class UnknownUriDispatcher : IOperationInvoker
    {
        public object[] AllocateInputs()
        {
            //no inputs are really going to come in,
            //but we want to provide an array anyways
            return new object[1]; 
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            var responeObject = new YourResponseObject()
            {
                Message = "Invalid Uri",
                Code = "Error",
            };
            Message result = Message.CreateMessage(MessageVersion.None, null, responeObject);
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
            outputs = new object[1]{responeObject};
            return result;
        }

        public System.IAsyncResult InvokeBegin(object instance, object[] inputs, System.AsyncCallback callback, object state)
        {
            throw new System.NotImplementedException();
        }

        public object InvokeEnd(object instance, out object[] outputs, System.IAsyncResult result)
        {
            throw new System.NotImplementedException();
        }

        public bool IsSynchronous
        {
            get { return true; }
        }
    }
}

Once you have these objects specified, you can now use the new factory within your svc's "markup":

<%@ ServiceHost Language="C#" Debug="true" Service="your.service.namespace.here" CodeBehind="myservice.svc.cs"
                Factory="your.namespace.here.CustomServiceHostFactory" %>

And that should be it. as long as your object "YourResponseObject" can be serialized, it's serialized representation will be sent back to the client.

查看更多
老娘就宠你
3楼-- · 2019-03-03 09:28

If you want to catch all the unhandled requests at a global level in WCF REST then you have to create a custom WebHttpBehavior and custom IOperationInvoker as described in this post.

If you want to return a custom error text with custom status code(404) you can also look into the WebOperationContext.OutgoingResponse property as described here.

查看更多
登录 后发表回答