zoukankan      html  css  js  c++  java
  • XCF之实用篇

    上集回顾

        上集中已经实现了XCF的基础,但是不难发现这样的实现没有多少实用意义。

        本集的重点就是讨论怎么把XCF实用化。

    准备Xsd

        想一下如果要定义一个xml来描述,那么需要哪些元素。

        首先是一个模板,这个模板描述了请求的总体结构。

        其次是变量,这些变量描述了请求中的变化值。

        然后是需要一个机制,将模板和变量融合起来,变成一个真正的请求实例。

        最后是对响应的处理,如果不关心响应的话,这部分可以省略。

        所以,可以简单的定义出这样的Schema:

    <?xml version="1.0" encoding="utf-8"?>
    <xs:schema id="WcfFantasia"
        targetNamespace="http://www.cnblogs.com/vwxyzh/WcfFantasia/"
        elementFormDefault="qualified"
        attributeFormDefault="qualified"
        xmlns:f="http://www.cnblogs.com/vwxyzh/WcfFantasia/"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      
      <xs:complexType name="Request">
        <xs:sequence>
          <xs:element name="template" type="xs:anyType"/>
          <xs:element name="transform" type="xs:anyType" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>
    
      <xs:complexType name="Response">
        <xs:sequence minOccurs="0" maxOccurs="unbounded">
          <xs:element name="item" type="f:ResponseItem"/>
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="ResponseItem">
        <xs:attribute name="name" type="xs:string" use="required"/>
        <xs:attribute name="path" type="xs:string" use="required"/>
      </xs:complexType>
    
      <xs:complexType name="RequestResponse">
        <xs:sequence>
          <xs:element name="request" type="f:Request"/>
          <xs:element name="response" type="f:Response"/>
        </xs:sequence>
        <xs:attribute name="method" type="xs:string" use="required"/>
        <xs:attribute name="address" type="xs:string" use="required"/>
      </xs:complexType>
    
      <xs:element name="root" type="f:RequestResponse"/>
    
      <xs:element name="bind">
        <xs:complexType>
          <xs:attribute name="value" type="xs:string" use="required"/>
        </xs:complexType>
      </xs:element>
    </xs:schema>
    

        其中,request中的template节允许承载任何内容,用于记录模板,而transform节主要承载一个Xslt,但是由于无法在Xsd中指定Xslt,就暂时用任意内容代替。

    XCF的请求/响应Xml实例

        看了xsd还在云里雾里?不妨直接看Xml实例吧,服务继续用上次的服务:

    <?xml version="1.0" encoding="utf-8"?>
    <f:root f:method="Echo"
            f:address="http://localhost:12345/EchoService/"
            xmlns:f="http://www.cnblogs.com/vwxyzh/WcfFantasia/">
      <f:request>
        <f:template>
          <Echo xmlns="urn:test">
            <p>
              <Age>
                <f:bind value="age"/>
              </Age>
              <Name>
                <f:bind value="name"/>
              </Name>
            </p>
          </Echo>
        </f:template>
        <f:transform>
          <xsl:stylesheet version="1.0"
                          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                          xmlns:f="http://www.cnblogs.com/vwxyzh/WcfFantasia/">
            <xsl:output omit-xml-declaration="yes" method="xml" encoding="utf-8"/>
            <xsl:template match="f:bind">
              <xsl:variable name="name" select="@value"/>
              <xsl:value-of select="f:GetContextValue($name)"/>
            </xsl:template>
            <xsl:template match="/|@*|node()">
              <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
              </xsl:copy>
            </xsl:template>
          </xsl:stylesheet>
        </f:transform>
      </f:request>
      <f:response>
        <f:item f:name="name" f:path="/a:EchoResponse/a:EchoResult/a:Name" xmlns:a="urn:test"/>
        <f:item f:name="age" f:path="/a:EchoResponse/a:EchoResult/a:Age" xmlns:a="urn:test"/>
      </f:response>
    </f:root>

        乍看上去是不是有点多?

        别急,接下来,一步一步的分析一下。

        首先,看一下template的内容:

          <Echo xmlns="urn:test">
            <p>
              <Age>
                <f:bind value="age"/>
              </Age>
              <Name>
                <f:bind value="name"/>
              </Name>
            </p>
          </Echo>
    

        是不是有点熟悉,去掉bind节,换成产量的话,这个就是上集中的请求内容。

        那么bind节是干什么的?就把它当成一个变量占位符,在后面的Xslt中会对这一节做运算,并替换内容。

        接着,看一下transform节的内容:

          <xsl:stylesheet version="1.0"
                          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                          xmlns:f="http://www.cnblogs.com/vwxyzh/WcfFantasia/">
            <xsl:output omit-xml-declaration="yes" method="xml" encoding="utf-8"/>
            <xsl:template match="f:bind">
              <xsl:variable name="name" select="@value"/>
              <xsl:value-of select="f:GetContextValue($name)"/>
            </xsl:template>
            <xsl:template match="/|@*|node()">
              <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
              </xsl:copy>
            </xsl:template>
          </xsl:stylesheet>
    

        这里使用了一个通用的转换方式,当然这里也可以使用特定转换。

        至于response节,暂时不作为重点介绍。

    实现XcfEngine

        简单的实现一下:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl;
    
    namespace WcfFantasia
    {
        public class XcfEngine
        {
    
            public const string XcfNamespace = "http://www.cnblogs.com/vwxyzh/WcfFantasia/";
            private readonly XcfChannelFactory m_factory;
    
            public XcfEngine(XcfChannelFactory factory)
            {
                m_factory = factory;
            }
    
            public Dictionary<string, object> Call(XmlReader reader, IDictionary<string, object> contexts)
            {
                XPathDocument doc = new XPathDocument(reader);
                XmlNameTable nameTable = reader.NameTable;
                XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
                namespaceManager.AddNamespace("f", XcfNamespace);
                var root = doc.CreateNavigator().SelectSingleNode("f:root", namespaceManager);
                using (var stream = new MemoryStream())
                {
                    CreateRequest(contexts, root, stream, namespaceManager);
                    stream.Seek(0L, SeekOrigin.Begin);
                    var address = root.SelectSingleNode("@f:address", namespaceManager).Value;
                    var method = root.SelectSingleNode("@f:method", namespaceManager).Value;
                    using (var c = m_factory.Create(new Uri(address)))
                    using (var requestReader = XmlReader.Create(stream))
                    using (var resp = c.Request(method, requestReader))
                    using (var respBody = resp.GetBody())
                        return ProcessResponse(root, namespaceManager, respBody);
                }
            }
    
            private void CreateRequest(IDictionary<string, object> contexts, XPathNavigator root, MemoryStream stream, XmlNamespaceManager namespaceManager)
            {
                var template = root.SelectSingleNode("f:request/f:template", namespaceManager).SelectChildren(XPathNodeType.Element);
                template.MoveNext();
                var transform = root.SelectSingleNode("f:request/f:transform", namespaceManager).SelectChildren(XPathNodeType.Element);
                transform.MoveNext();
                using (var input = template.Current.ReadSubtree())
                using (var trans = transform.Current.ReadSubtree())
                    Transform(trans, input, stream, contexts);
            }
    
            private void Transform(XmlReader transform, XmlReader input,
                Stream result, IDictionary<string, object> dict)
            {
                var f = new XslCompiledTransform();
                f.Load(transform);
                XsltArgumentList xal = new XsltArgumentList();
                xal.AddExtensionObject(XcfNamespace, new XcfContext(dict));
                f.Transform(input, xal, result);
            }
    
            private static Dictionary<string, object> ProcessResponse(XPathNavigator root, XmlNamespaceManager namespaceManager, XmlReader respBody)
            {
                Dictionary<string, object> result = new Dictionary<string, object>();
                XPathDocument respDoc = new XPathDocument(respBody);
                foreach (XPathNavigator item in root.Select("f:response/f:item", namespaceManager))
                {
                    foreach (var nspair in item.GetNamespacesInScope(XmlNamespaceScope.Local))
                        namespaceManager.AddNamespace(nspair.Key, nspair.Value);
                    string xpath = item.GetAttribute("path", XcfNamespace);
                    string name = item.GetAttribute("name", XcfNamespace);
                    var itemNode = respDoc.CreateNavigator().SelectSingleNode(xpath, namespaceManager);
                    if (itemNode != null)
                        result[name] = itemNode.Value;
                    else
                        result[name] = null;
                }
                return result;
            }
    
        }
    }
    
    public class XcfContext
    {
        private Dictionary<string, object> m_dict;
    
        public XcfContext(IDictionary<string, object> dict)
        {
            m_dict = new Dictionary<string, object>(dict);
        }
    
        public object GetContextValue(string key)
        {
            object result;
            m_dict.TryGetValue(key, out result);
            return result ?? string.Empty;
        }
    
    }
    

        整个XcfEngine就完成了,来看看怎么用吧:

    using (var f = new XcfChannelFactory(new WSHttpBinding()))
    {
        XcfEngine engine = new XcfEngine(f);
        using (var reader = XmlReader.Create("sample.xml"))
        {
            var result = engine.Call(reader, new Dictionary<string, object>
            {
                { "age", 1 },
                { "name", "xcf" },
            });
            foreach (var item in result)
                Console.WriteLine("Name={0}, Value={1}", item.Key, item.Value);
        }
    }
    

        这样就把整个xcf调用起来了,当然变量部分目前直接使用Dictionary,当然可以使用更好的方式,毕竟这个只是初步的实现。

    XCF展望

        说了三篇的XCF,那么定位是什么哪?

        无契约类型的优势是什么?或者说把契约还原成xml的优势是什么?

        首先,xml而不是类型就不依赖编译,也就是当对方契约变化的时候,可以直接更新,这个特性看起来不怎么重要,但是在特定的场合下非常重要。

        其次,xml可以有更多的存储/发布方式,例如数据库或web/wcf等各种方式。

        再次,xml本身是一整套解决方案,可以用各种Xml的应用组合出更强大的xml应用。

        最后,可以利用xml调用那些编译程序时完全未知的web/wcf服务。

  • 相关阅读:
    i++ 与++i
    jquery下的domcument
    jquery
    MVC MVP MVVM
    两个for还是一个for?
    华为云服务器FTP连接
    vue-i18n 使用方法
    在本地运行vue build 文件
    vue项目中使用模拟数据 MOCK
    超简单 超详细 vue项目中使用svg图标 阿里巴巴图标库
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/1805776.html
Copyright © 2011-2022 走看看