Обработка 404 в WCF Rest Service - PullRequest
5 голосов
/ 16 июня 2011

У меня есть служба отдыха wcf на IIS 7.5. Когда кто-то посещает часть конечной точки, которая не существует (т. Е. http://localhost/rest.svc/DOESNOTEXIST против http://localhost/EXISTS), он получает страницу с серой и синей ошибкой общего WCF с кодом состояния 404. Однако я хотел бы вернуть что-то вроде следующего:

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

Я попытался настроить пользовательские ошибки в IIS, но они работают только при запросе страницы за пределами службы остальных (т. Е. http://localhost/DOESNOTEXIST).

Кто-нибудь знает, как это сделать?

Редактировать После ответа ниже я смог выяснить, что мне нужно создать класс WebHttpExceptionBehaviorElement, который реализует BehaviorExtensionElement.

 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 /> 

к поведению моей конечной точки по умолчанию.

Спасибо

Джеффри Кевин Прай

Ответы [ 2 ]

5 голосов
/ 16 июня 2011

Сначала создайте пользовательское поведение, которое подклассов 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; }
    }
}

На этом этапе вы должны связать новое поведение с вашим сервисом.

Есть несколько способов сделать это, поэтому просто спросите, если вы еще не знаете, и я с удовольствием уточню дальше.

1 голос
/ 20 июля 2012

У меня была похожая проблема, и другой ответ действительно привел к моему возможному успеху, это был не самый ясный из ответов.Ниже описан способ решения этой проблемы.

Мои проекты настроены как служба WCF, размещенная как svc, размещенная в IIS.Я не мог пойти с маршрутом конфигурации для добавления поведения, потому что мои версии сборок меняются при каждой регистрации из-за непрерывной интеграции.

Чтобы преодолеть эту проблему, я создал собственный 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» может быть сериализован, его сериализованное представление будет отправлено обратно клиенту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...