使用自定义 WCF 正文反序列化而不更改 URI 模板反序列化

2024-01-04

From 这篇博文 http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/03/wcf-extensibility-message-formatters.aspx,我能够创建自定义 WCFIDispatchMessageFormatter使用 JSON.NET 序列化。它效果很好,但有一个警告:使用它UriTemplate不一定按预期工作。

这是博客文章提供的实现:

class NewtonsoftJsonDispatchFormatter : IDispatchMessageFormatter
{
    private readonly OperationDescription od;
    private readonly ServiceEndpoint ep;
    private readonly Dictionary<string, int> parameterNames = new Dictionary<string, int>();

    public NewtonsoftJsonDispatchFormatter(OperationDescription od, ServiceEndpoint ep, bool isRequest)
    {
        this.od = od;
        this.ep = ep;
        if (isRequest)
        {
            int operationParameterCount = od.Messages[0].Body.Parts.Count;
            if (operationParameterCount > 1)
            {
                this.parameterNames = new Dictionary<string, int>();
                for (int i = 0; i < operationParameterCount; i++)
                {
                    this.parameterNames.Add(od.Messages[0].Body.Parts[i].Name, i);
                }
            }
        }
    }
    public void DeserializeRequest(Message message, object[] parameters)
    {
        if (message.IsEmpty) 
            return;

        object bodyFormatProperty;

        if (!message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out bodyFormatProperty) ||
            (bodyFormatProperty as WebBodyFormatMessageProperty).Format != WebContentFormat.Raw)
        {
            throw new InvalidOperationException("Incoming messages must have a body format of Raw. Is a ContentTypeMapper set on the WebHttpBinding?");
        }

        XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
        bodyReader.ReadStartElement("Binary");
        byte[] rawBody = bodyReader.ReadContentAsBase64();

        using (MemoryStream ms = new MemoryStream(rawBody))
        using (StreamReader sr = new StreamReader(ms))
        {
            if (parameters.Length == 1)
                parameters[0] = Helper.serializer.Deserialize(sr, od.Messages[0].Body.Parts[0].Type);
            else
            {
                // multiple parameter, needs to be wrapped
                using (Newtonsoft.Json.JsonReader reader = new Newtonsoft.Json.JsonTextReader(sr))
                {
                    reader.Read();
                    if (reader.TokenType != Newtonsoft.Json.JsonToken.StartObject)
                        throw new InvalidOperationException("Input needs to be wrapped in an object");
                    reader.Read();
                    while (reader.TokenType == Newtonsoft.Json.JsonToken.PropertyName)
                    {
                        string parameterName = reader.Value as string;
                        reader.Read();
                        if (this.parameterNames.ContainsKey(parameterName))
                        {
                            int parameterIndex = this.parameterNames[parameterName];
                            parameters[parameterIndex] = Helper.serializer.Deserialize(reader, this.od.Messages[0].Body.Parts[parameterIndex].Type);
                        }
                        else
                            reader.Skip();
                        reader.Read();
                    }
                }
            }
        }
    }

     public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) { ... }
}

基本上,object[] parameters in the DeserializeMethod签名是out该方法需要实例化的参数。

因此,这可以很好地处理如下所示的 REST 端点:

[WebInvoke(Method="POST", UriTemplate="foo/")]
public Foo MakeFoo(Foo foo) { ... }

或者像这样:

[WebInvoke(Method="POST", UriTemplate="FooBar/")]
public FooBar FooBar(Foo foo, Bar bar) { .. }

但目前它不会将 URI 模板参数映射到方法参数,例如像这样的东西:

[WebGet(UriTemplate="Foo/{id}")]
public Foo GetFoo(string id) { ... }

微软在重写中写道GetRequestDispatchFormatter https://msdn.microsoft.com/en-us/library/system.servicemodel.description.webhttpbehavior.getrequestdispatchformatter(v=vs.110).aspx:

这是一个扩展点,派生行为可以使用它来提供自己的 IDispatchMessageFormatter 实现,调用该实现来从请求消息中反序列化服务操作的输入参数。服务操作的 UriTemplate 中指定的参数必须从请求消息的 To URI 中反序列化,其他参数必须从请求消息的正文中反序列化。

很好。我更新了消息正文中参数的反序列化。但我不想覆盖反序列化中的参数UriTemplate。有没有办法使用现有代码以默认方式将传入的 URI 请求映射到参数UriTemplate被处理?

看来我需要使用类似的东西UriTemplateDispatchFormatter http://referencesource.microsoft.com/#System.ServiceModel.Web/System/ServiceModel/Dispatcher/UriTemplateDispatchFormatter.cs但我不知道如何实现这一点,而且它是非公开的。


好吧,这可能是我做过的最荒谬的事情,但是复制源代码UriTemplateDispatchFormatter,你可以简单地返回一个UriTemplateDispatchFormatter带着“内在”IDispatchFormatter对应于IDispatchFormatter我这里提供了。不知道为什么这个类是内部的>_>

以下类定义:

class UriTemplateDispatchFormatter : IDispatchMessageFormatter
{
    internal Dictionary<int, string> pathMapping;
    internal Dictionary<int, KeyValuePair<string, Type>> queryMapping;
    Uri baseAddress;
    IDispatchMessageFormatter bodyFormatter;
    string operationName;
    QueryStringConverter qsc;
    int totalNumUTVars;
    UriTemplate uriTemplate;

    public UriTemplateDispatchFormatter(OperationDescription operationDescription, IDispatchMessageFormatter bodyFormatter, QueryStringConverter qsc, string contractName, Uri baseAddress)
    {
        this.bodyFormatter = bodyFormatter;
        this.qsc = qsc;
        this.baseAddress = baseAddress;
        this.operationName = operationDescription.Name;
        Populate(
            out this.pathMapping,
            out this.queryMapping,
            out this.totalNumUTVars,
            out this.uriTemplate,
            operationDescription,
            qsc,
            contractName);
    }

    public void DeserializeRequest(Message message, object[] parameters)
    {
        object[] bodyParameters = new object[parameters.Length - this.totalNumUTVars];

        if (bodyParameters.Length != 0)
        {
            this.bodyFormatter.DeserializeRequest(message, bodyParameters);
        }
        int j = 0;
        UriTemplateMatch utmr = null;
        string UTMRName = "UriTemplateMatchResults";
        if (message.Properties.ContainsKey(UTMRName))
        {
            utmr = message.Properties[UTMRName] as UriTemplateMatch;
        }
        else
        {
            if (message.Headers.To != null && message.Headers.To.IsAbsoluteUri)
            {
                utmr = this.uriTemplate.Match(this.baseAddress, message.Headers.To);
            }
        }
        NameValueCollection nvc = (utmr == null) ? new NameValueCollection() : utmr.BoundVariables;
        for (int i = 0; i < parameters.Length; ++i)
        {
            if (this.pathMapping.ContainsKey(i) && utmr != null)
            {
                parameters[i] = nvc[this.pathMapping[i]];
            }
            else if (this.queryMapping.ContainsKey(i) && utmr != null)
            {
                string queryVal = nvc[this.queryMapping[i].Key];
                parameters[i] = this.qsc.ConvertStringToValue(queryVal, this.queryMapping[i].Value);
            }
            else
            {
                parameters[i] = bodyParameters[j];
                ++j;
            }
        }
    }


    public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
    {
        throw new NotImplementedException();
    }

    private static void Populate(out Dictionary<int, string> pathMapping,
    out Dictionary<int, KeyValuePair<string, Type>> queryMapping,
    out int totalNumUTVars,
    out UriTemplate uriTemplate,
    OperationDescription operationDescription,
    QueryStringConverter qsc,
    string contractName)
    {
        pathMapping = new Dictionary<int, string>();
        queryMapping = new Dictionary<int, KeyValuePair<string, Type>>();
        string utString = GetUTStringOrDefault(operationDescription);
        uriTemplate = new UriTemplate(utString);
        List<string> neededPathVars = new List<string>(uriTemplate.PathSegmentVariableNames);
        List<string> neededQueryVars = new List<string>(uriTemplate.QueryValueVariableNames);
        Dictionary<string, byte> alreadyGotVars = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase);
        totalNumUTVars = neededPathVars.Count + neededQueryVars.Count;
        for (int i = 0; i < operationDescription.Messages[0].Body.Parts.Count; ++i)
        {
            MessagePartDescription mpd = operationDescription.Messages[0].Body.Parts[i];
            string parameterName = XmlConvert.DecodeName(mpd.Name);
            if (alreadyGotVars.ContainsKey(parameterName))
            {
                throw new InvalidOperationException();
            }
            List<string> neededPathCopy = new List<string>(neededPathVars);
            foreach (string pathVar in neededPathCopy)
            {
                if (string.Compare(parameterName, pathVar, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    if (mpd.Type != typeof(string))
                    {
                        throw new InvalidOperationException();
                    }
                    pathMapping.Add(i, parameterName);
                    alreadyGotVars.Add(parameterName, 0);
                    neededPathVars.Remove(pathVar);
                }
            }
            List<string> neededQueryCopy = new List<string>(neededQueryVars);
            foreach (string queryVar in neededQueryCopy)
            {
                if (string.Compare(parameterName, queryVar, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    if (!qsc.CanConvert(mpd.Type))
                    {
                        throw new InvalidOperationException();
                    }
                    queryMapping.Add(i, new KeyValuePair<string, Type>(parameterName, mpd.Type));
                    alreadyGotVars.Add(parameterName, 0);
                    neededQueryVars.Remove(queryVar);
                }
            }
        }
        if (neededPathVars.Count != 0)
        {
            throw new InvalidOperationException();
        }
        if (neededQueryVars.Count != 0)
        {
            throw new InvalidOperationException();
        }
    }
    private static string GetUTStringOrDefault(OperationDescription operationDescription)
    {
        string utString = GetWebUriTemplate(operationDescription);
        if (utString == null && GetWebMethod(operationDescription) == "GET")
        {
            utString = MakeDefaultGetUTString(operationDescription);
        }
        if (utString == null)
        {
            utString = operationDescription.Name;
        }
        return utString;
    }
    private static string MakeDefaultGetUTString(OperationDescription od)
    {
        StringBuilder sb = new StringBuilder(XmlConvert.DecodeName(od.Name));
        //sb.Append("/*"); // note: not + "/*", see 8988 and 9653
        if (!IsUntypedMessage(od.Messages[0]))
        {
            sb.Append("?");
            foreach (MessagePartDescription mpd in od.Messages[0].Body.Parts)
            {
                string parameterName = XmlConvert.DecodeName(mpd.Name);
                sb.Append(parameterName);
                sb.Append("={");
                sb.Append(parameterName);
                sb.Append("}&");
            }
            sb.Remove(sb.Length - 1, 1);
        }
        return sb.ToString();
    }
    private static bool IsUntypedMessage(MessageDescription message)
    {

        if (message == null)
        {
            return false;
        }
        return (message.Body.ReturnValue != null && message.Body.Parts.Count == 0 && message.Body.ReturnValue.Type == typeof(Message)) ||
            (message.Body.ReturnValue == null && message.Body.Parts.Count == 1 && message.Body.Parts[0].Type == typeof(Message));
    }
    private static void EnsureOk(WebGetAttribute wga, WebInvokeAttribute wia, OperationDescription od)
    {
        if (wga != null && wia != null)
        {
            throw new InvalidOperationException();
        }
    }
    private static string GetWebUriTemplate(OperationDescription od)
    {
        // return exactly what is on the attribute
        WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>();
        WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>();
        EnsureOk(wga, wia, od);
        if (wga != null)
        {
            return wga.UriTemplate;
        }
        else if (wia != null)
        {
            return wia.UriTemplate;
        }
        else
        {
            return null;
        }
    }
    private static string GetWebMethod(OperationDescription od)
    {
        WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>();
        WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>();
        EnsureOk(wga, wia, od);
        if (wga != null)
        {
            return "GET";
        }
        else if (wia != null)
        {
            return wia.Method ?? "POST";
        }
        else
        {
            return "POST";
        }
    }

}

以及以下行为:

class NewtonsoftJsonBehavior : WebHttpBehavior
{
    protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
    {
        return new UriTemplateDispatchFormatter(
            operationDescription,
            new NewtonsoftJsonDispatchFormatter(operationDescription, endpoint, true),
            GetQueryStringConverter(operationDescription),
            endpoint.Contract.Name,
            endpoint.Address.Uri);
    }

    protected override IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription od, ServiceEndpoint ep)
    {
        return new NewtonsoftJsonDispatchFormatter(od, ep, false);
    }

}

works

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用自定义 WCF 正文反序列化而不更改 URI 模板反序列化 的相关文章

  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • 类型中的属性名称必须是唯一的

    我正在使用 Entity Framework 5 并且有以下实体 public class User public Int32 Id get set public String Username get set public virtual
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • 随着时间的推移,添加到 List 变得非常慢

    我正在解析一个大约有 1000 行的 html 表 我从一个字符串中添加 10 个字符串 td 每行到一个list td
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 空指针与 int 等价

    Bjarne 在 C 编程语言 中写道 空指针与整数零不同 但 0 可以用作空指针的指针初始值设定项 这是否意味着 void voidPointer 0 int zero 0 int castPointer reinterpret cast
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • C# 使用“?” if else 语句设置值这叫什么

    嘿 我刚刚看到以下声明 return name null name NA 我只是想知道这在 NET 中叫什么 是吗 代表即然后执行此操作 这是一个俗称的 条件运算符 三元运算符 http en wikipedia org wiki Tern
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐