在 WCF 休息服务中处理 404

Posted

技术标签:

【中文标题】在 WCF 休息服务中处理 404【英文标题】:Handling 404 in WCF Rest Service 【发布时间】:2011-09-16 08:14:33 【问题描述】:

我在 IIS 7.5 上有一个 wcf 休息服务。当有人访问不存在的端点的一部分时(即http://localhost/rest.svc/DOESNOTEXIST vs http://localhost/EXISTS),他们会看到一个带有状态代码 404 的通用 WCF 灰色和蓝色错误页面。但是,我想返回类似以下:

<service-response>
   <error>The url requested does not exist</error>
</service-response>

我尝试在 IIS 中配置自定义错误,但它们仅在请求 REST 服务之外的页面时才有效(即http://localhost/DOESNOTEXIST)。

有人知道怎么做吗?

编辑 在下面的答案之后,我能够弄清楚我需要创建一个实现 BehaviorExtensionElement 的 WebHttpExceptionBehaviorElement 类。

 public class WebHttpExceptionBehaviorElement : BehaviorExtensionElement
 
    ///  
    /// Get the type of behavior to attach to the endpoint  
    ///  
    public override Type BehaviorType
    
        get
        
            return typeof(WebHttpExceptionBehavior);
        
    

    ///  
    /// Create the custom behavior  
    ///  
    protected override object CreateBehavior()
    
        return new WebHttpExceptionBehavior();
      
 

然后我可以通过以下方式在我的 web.config 文件中引用它:

<extensions>
  <behaviorExtensions>
    <add name="customError" type="Service.WebHttpExceptionBehaviorElement, Service"/>
  </behaviorExtensions>
</extensions>

然后添加

<customError /> 

到我的默认端点行为。

谢谢,

杰弗里·凯文·普瑞

【问题讨论】:

您使用的是 REST Starter 工具包还是 WCF Web API? 【参考方案1】:

首先,创建一个子类 WebHttpBehavior 的自定义行为 - 在这里您将删除默认的 Unhandled Dispatch Operation 处理程序,并附加您自己的处理程序:

public class WebHttpBehaviorEx : WebHttpBehavior

    public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    
        base.ApplyDispatchBehavior(endpoint, endpointDispatcher);

        endpointDispatcher.DispatchRuntime.Operations.Remove(endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation);
        endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation = new DispatchOperation(endpointDispatcher.DispatchRuntime, "*", "*", "*");
        endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.DeserializeRequest = false;
        endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.SerializeReply = false;
        endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.Invoker = new UnknownOperationInvoker();

    

那么。制作您未知的操作处理程序。此类将处理未知操作请求并生成作为响应的“消息”。我已经展示了如何创建纯文本消息。为您的目的修改它应该是相当简单的:

internal class UnknownOperationInvoker : IOperationInvoker

    public object[] AllocateInputs()
    
        return new object[1];
    


    private Message CreateTextMessage(string message)
    
        Message result = Message.CreateMessage(MessageVersion.None, null, new HelpPageGenerator.TextBodyWriter(message));
        result.Properties["WebBodyFormatMessageProperty"] = new WebBodyFormatMessageProperty(WebContentFormat.Raw);
        WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
        return result;
    

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    
        // Code HERE

                StringBuilder builder = new System.Text.StringBuilder();

                builder.Append("...");

                Message result = CreateTextMessage(builder.ToString());

                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; 
    

此时您必须将新行为与您的服务相关联。

有几种方法可以做到这一点,所以如果您还不知道,请询问,我会很乐意进一步详细说明。

【讨论】:

在调用部分我应该为输入和输出分配什么? 我在哪里关联配置中的网络行为?这就是我现在所拥有的... ... .. . 感谢您的帮助! 我会用更多细节更新答案:你的 cmets - 但简而言之,看看我如何“返回结果;”在调用方法中?它返回一种“消息”......这就是你想要的。此链接:bit.ly/AwBaK 有使用网络配置添加新客户行为的描述。 谢谢...但是在调用方法中,您也有 out object[] 输出作为参数。它不允许我返回空输出。我正在回一条消息。我将等待您的实施回复以关联行为。再次感谢您的所有帮助! 很抱歉,我在上班时被撞了……很高兴你明白了。【参考方案2】:

我遇到了类似的问题,而另一个答案确实导致了我最终的成功,这不是最明确的答案。以下是我解决此问题的方法。

我的项目设置是作为托管在 IIS 中的 svc 托管的 WCF 服务。我无法使用添加行为的配置路线,因为我的程序集版本由于持续集成而在每次签入时都会更改。

为了克服这个障碍,我创建了一个自定义 ServiceHostFactory:

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;
        
    

正如您在上面看到的,我们正在添加一个新行为:WcfUnknownUriBehavior。这个新的自定义行为的核心职责是替换 UnknownDispatcher。下面是那个实现:

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; 
        
    

一旦您指定了这些对象,您现在可以在您的 svc 的“标记”中使用新工厂:

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

应该就是这样。只要您的对象“YourResponseObject”可以被序列化,它的序列化表示就会被发送回客户端。

【讨论】:

大声笑...您发布了我一年前答案的变体并否决了我的答案?好吧,我猜。 哎呀。对不起。并不是要否决您的答案。我一定是胖手指了。我很抱歉。

以上是关于在 WCF 休息服务中处理 404的主要内容,如果未能解决你的问题,请参考以下文章

删除不工作在 WCF 休息服务抛出 (405) 方法不允许

IIS Express错误http 404.17 - 未找到运行WCF服务

为啥 WCF 休息服务(不是使用 WCF 休息服务模板创建的)不起作用?

WCF 休息和 Wcf 肥皂服务之间的区别

wcf 休息服务响应中的 xml 被更改 为啥?

WCF 服务在方法请求上返回 404