zoukankan      html  css  js  c++  java
  • Webservices-2.C#创建web服务,及引用访问、代码访问

    注:web服务简介Webservices-1.web服务定义简介

    以下均以C#语言为例

    一、创建web服务(简单介绍,主要讨论客户端引用)

      打开VS创建网站项目,在网站项目中添加“WEB服务(ASMX)”,

      此时VS便已经默认建立好一个web服务。

      如需使用Session,请添加“[WebMethod(EnableSession=true)] ”,更多信息请实际编写代码时候查看智能提示,及msdn文档

      此时可以将服务部署于web服务器上,以IIS为例,部署完毕即可正确访问。有时创建IIS网站时,请注意配置使用的.net版本库。

      访问地址:http://172.168.0.40:8086/WebSer.asmx(个人PC示例,请勿随意copy)

    二、服务解析概述

      在访问地址后添加?WSDL既可以查看服务说明文档http://172.168.0.40:8086/WebSer.asmx?WSDL

      

      1 <?xml version="1.0" encoding="utf-8"?>
      2 <wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="ChineShine" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="ChineShine" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
      3   <wsdl:types>
      4     <s:schema elementFormDefault="qualified" targetNamespace="ChineShine">
      5       <s:element name="HelloWorld">
      6         <s:complexType>
      7           <s:sequence>
      8             <s:element minOccurs="0" maxOccurs="1" name="ss" type="s:string" />
      9           </s:sequence>
     10         </s:complexType>
     11       </s:element>
     12       <s:element name="HelloWorldResponse">
     13         <s:complexType>
     14           <s:sequence>
     15             <s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
     16           </s:sequence>
     17         </s:complexType>
     18       </s:element>
     19       <s:element name="NoReturnValue">
     20         <s:complexType>
     21           <s:sequence>
     22             <s:element minOccurs="0" maxOccurs="1" name="ss" type="s:string" />
     23           </s:sequence>
     24         </s:complexType>
     25       </s:element>
     26       <s:element name="NoReturnValueResponse">
     27         <s:complexType />
     28       </s:element>
     29       <s:element name="NoParaValue">
     30         <s:complexType />
     31       </s:element>
     32       <s:element name="NoParaValueResponse">
     33         <s:complexType>
     34           <s:sequence>
     35             <s:element minOccurs="0" maxOccurs="1" name="NoParaValueResult" type="s:string" />
     36           </s:sequence>
     37         </s:complexType>
     38       </s:element>
     39     </s:schema>
     40   </wsdl:types>
     41   <wsdl:message name="HelloWorldSoapIn">
     42     <wsdl:part name="parameters" element="tns:HelloWorld" />
     43   </wsdl:message>
     44   <wsdl:message name="HelloWorldSoapOut">
     45     <wsdl:part name="parameters" element="tns:HelloWorldResponse" />
     46   </wsdl:message>
     47   <wsdl:message name="NoReturnValueSoapIn">
     48     <wsdl:part name="parameters" element="tns:NoReturnValue" />
     49   </wsdl:message>
     50   <wsdl:message name="NoReturnValueSoapOut">
     51     <wsdl:part name="parameters" element="tns:NoReturnValueResponse" />
     52   </wsdl:message>
     53   <wsdl:message name="NoParaValueSoapIn">
     54     <wsdl:part name="parameters" element="tns:NoParaValue" />
     55   </wsdl:message>
     56   <wsdl:message name="NoParaValueSoapOut">
     57     <wsdl:part name="parameters" element="tns:NoParaValueResponse" />
     58   </wsdl:message>
     59   <wsdl:portType name="WebSerSoap">
     60     <wsdl:operation name="HelloWorld">
     61       <wsdl:input message="tns:HelloWorldSoapIn" />
     62       <wsdl:output message="tns:HelloWorldSoapOut" />
     63     </wsdl:operation>
     64     <wsdl:operation name="NoReturnValue">
     65       <wsdl:input message="tns:NoReturnValueSoapIn" />
     66       <wsdl:output message="tns:NoReturnValueSoapOut" />
     67     </wsdl:operation>
     68     <wsdl:operation name="NoParaValue">
     69       <wsdl:input message="tns:NoParaValueSoapIn" />
     70       <wsdl:output message="tns:NoParaValueSoapOut" />
     71     </wsdl:operation>
     72   </wsdl:portType>
     73   <wsdl:binding name="WebSerSoap" type="tns:WebSerSoap">
     74     <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
     75     <wsdl:operation name="HelloWorld">
     76       <soap:operation soapAction="ChineShine/HelloWorld" style="document" />
     77       <wsdl:input>
     78         <soap:body use="literal" />
     79       </wsdl:input>
     80       <wsdl:output>
     81         <soap:body use="literal" />
     82       </wsdl:output>
     83     </wsdl:operation>
     84     <wsdl:operation name="NoReturnValue">
     85       <soap:operation soapAction="ChineShine/NoReturnValue" style="document" />
     86       <wsdl:input>
     87         <soap:body use="literal" />
     88       </wsdl:input>
     89       <wsdl:output>
     90         <soap:body use="literal" />
     91       </wsdl:output>
     92     </wsdl:operation>
     93     <wsdl:operation name="NoParaValue">
     94       <soap:operation soapAction="ChineShine/NoParaValue" style="document" />
     95       <wsdl:input>
     96         <soap:body use="literal" />
     97       </wsdl:input>
     98       <wsdl:output>
     99         <soap:body use="literal" />
    100       </wsdl:output>
    101     </wsdl:operation>
    102   </wsdl:binding>
    103   <wsdl:binding name="WebSerSoap12" type="tns:WebSerSoap">
    104     <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    105     <wsdl:operation name="HelloWorld">
    106       <soap12:operation soapAction="ChineShine/HelloWorld" style="document" />
    107       <wsdl:input>
    108         <soap12:body use="literal" />
    109       </wsdl:input>
    110       <wsdl:output>
    111         <soap12:body use="literal" />
    112       </wsdl:output>
    113     </wsdl:operation>
    114     <wsdl:operation name="NoReturnValue">
    115       <soap12:operation soapAction="ChineShine/NoReturnValue" style="document" />
    116       <wsdl:input>
    117         <soap12:body use="literal" />
    118       </wsdl:input>
    119       <wsdl:output>
    120         <soap12:body use="literal" />
    121       </wsdl:output>
    122     </wsdl:operation>
    123     <wsdl:operation name="NoParaValue">
    124       <soap12:operation soapAction="ChineShine/NoParaValue" style="document" />
    125       <wsdl:input>
    126         <soap12:body use="literal" />
    127       </wsdl:input>
    128       <wsdl:output>
    129         <soap12:body use="literal" />
    130       </wsdl:output>
    131     </wsdl:operation>
    132   </wsdl:binding>
    133   <wsdl:service name="WebSer">
    134     <wsdl:port name="WebSerSoap" binding="tns:WebSerSoap">
    135       <soap:address location="http://172.168.0.40:8086/WebSer.asmx" />
    136     </wsdl:port>
    137     <wsdl:port name="WebSerSoap12" binding="tns:WebSerSoap12">
    138       <soap12:address location="http://172.168.0.40:8086/WebSer.asmx" />
    139     </wsdl:port>
    140   </wsdl:service>
    141 </wsdl:definitions>
    View Code

      

     折叠后查看

      

      注意:WSDL元素[1]基于XML语法描述了与服务进行交互的基本元素:

        Type(消息类型):数据类型定义的容器,它使用某种类型系统(如XSD)。
        Message(消息):通信数据的抽象类型化定义,它由一个或者多个part组成。
        Part:消息参数
        Operation(操作):对服务所支持的操作进行抽象描述,WSDL定义了四种操作: 1.单向(one-way):端点接受信息;2.请求-响应(request-response):端点接受消息,然后发送相关消息;3.要求-响应(solicit-response):端点发送消息,然后接受相关消息;4.通知(notification[2]):端点发送消息。
        Port Type (端口类型):特定端口类型的具体协议和数据格式规范。
        Binding:特定端口类型的具体协议和数据格式规范
        Port:定义为绑定和网络地址组合的单个端点。
        Service:相关端口的集合,包括其关联的接口、操作、消息等。

      2.1 targerNamespasce

        主要声明的是:本服务使用的命名空间 ,默认是:http://tempuri.org/ (打开一看,跑到bing去了,呵呵),可以自定义使用,如本文定义了:ChineShine

      2.2 wsdl:types

        主要是:服务接口里面的:方法名称,参数,返回值介绍

      2.3 wsdl:Message、wsdl:portType (参看以上注意)

      2.4 wsdl:Binding

        可以使用的协议访问,如示例中提示使用的soap(即soap1.1),soap12(即soap1.2)

      2.5 wsdl:service

        主要是:各种方式服务的接口地址

      综述:其实在调用别人web服务的时候,只要有web服务地址,即可能够使用,对方的服务,要详细正确使用还需服务开发者,提供详细调用文档。

    三、客户端服务引用(这里才是重点啊)

      客户端引用主要包含:项目中引用服务地址,普通引用(主要指VS中的“添加服务引用”)

    名称   使用场景 优缺点
    普通引用   项目建立初期 初期建立,方便快捷,易于使用
     使用代理类    完善的项目,使用中的项目,

    需求变更的项目,当然初期的也可以

     需要自己手工编写代码,

    对于成熟使用的项目扩展性好

    代码引用 post

    完善的项目,使用中的项目,

    需求变更的项目,当然初期的也可以

    需要自己手工编写代码,

    对于成熟使用的项目扩展性好

    get
    soap1.1
    soap1.2

       3.1 普通引用

        在需要引用的VS项目中,引用→添加服务引用→添加服务地址“http://172.168.0.40:8086/WebSer.asmx”,

        编写基本代码即可使用

        TestServiceReference.WebSerSoapClient wc = new TestServiceReference.WebSerSoapClient();//初始化web服务客户端对象
                string ss=wc.HelloWorld("你好");//调用web服务的,接口方法  

        说明:这种方法适用于VS操作,其他语言及IDE未测试。优点方便快捷,简单易于使用。

      3.2 代码引用

        这里以soap1.1 为例,post,get比较简单,soap1.2 与soap1.1类似,讲述soap1.1后会添加全部代码

        

        打开其中一个接口,参看

      

      

      

      注:前提条件,根据接口的wsdl,以及开发商提供的文档可以查看到:接口的地址(URL)、方法名(Method)、参数列表(Paras)、命名空间等信息(tns)

      编写请求代码:

        //创建一个HttpWebRequest
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
        request.Method = "POST";//请求方式
        request.ContentType = "text/xml; charset=utf-8";//网络文件的类型和网页的编码
        request.Headers.Add("SOAPAction", """ + XmlNs + (XmlNs.EndsWith("/") ? "" : "/") + MethodName + """);//构建soap1.1 请求头文件
        request.Credentials = CredentialCache.DefaultCredentials;//获取系统凭据
        request.Timeout = 10000;//请求超时时间
        byte[] data = EncodeParsToSoap(Pars, XmlNs, MethodName, soapVersion);//组织发送内容,并序列化成字节流,详细参看附注代码,
        request.ContentLength = data.Length;//请求数据长度
                using (Stream writer = request.GetRequestStream())//请求参数
                {
                    writer.Write(data, 0, data.Length);
                    writer.Close();
                }
        XmlDocument doc = new XmlDocument(), doc2 = new XmlDocument();
        doc = ReadXmlResponse(request.GetResponse());//接收返回消息  request.GetResponse() 附注代码已修改成使用异步编程方式
        这是一次请求,就完成了。详细处理,请参看详细代码

      附注:详细代码(复制到程序中,直接就可以使用哦!)

      1 using System;
      2 using System.Collections;
      3 using System.Collections.Generic;
      4 using System.IO;
      5 using System.Linq;
      6 using System.Net;
      7 using System.Text;
      8 using System.Web;
      9 using System.Xml;
     10 using System.Xml.Serialization;
     11 
     12 namespace TestWS
     13 {
     14     public class WebServicesCodeTool
     15     {
     16         private System.Collections.Hashtable _xmlNamespaces = new System.Collections.Hashtable();//缓存xmlNamespace,避免重复调用GetNamespace
     17 
     18         /// <summary>
     19         /// 需要WebService支持Post调用
     20         /// </summary>
     21         public System.Xml.XmlDocument QueryPostWebService(String URL, String MethodName, System.Collections.Hashtable Pars)
     22         {
     23             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL + "/" + MethodName);
     24             request.Method = "POST";
     25             request.ContentType = "application/x-www-form-urlencoded";
     26             SetWebRequest(request);
     27             byte[] data = EncodePars(Pars);
     28             WriteRequestData(request, data);
     29             return ReadXmlResponse(request.GetResponse());//这里没有修改成异步方式,建议自行修改
     30         }
     31 
     32         /// <summary>
     33         /// 需要WebService支持Get调用
     34         /// </summary>
     35         public XmlDocument QueryGetWebService(String URL, String MethodName, Hashtable Pars)
     36         {
     37             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL + "/" + MethodName + "?" + ParsToString(Pars));
     38             request.Method = "GET";
     39             request.ContentType = "application/x-www-form-urlencoded";
     40             SetWebRequest(request);
     41             return ReadXmlResponse(request.GetResponse());
     42         }
     43         /// <summary>
     44         /// 通用WebService调用(Soap),参数Pars为String类型的参数名、参数值、soap版本(如果支持1.2,使用1.2,默认填写1.1)
     45         /// </summary>
     46         /// <param name="URL"></param>
     47         /// <param name="MethodName"></param>
     48         /// <param name="Pars"></param>
     49         /// <param name="soapVersion"></param>
     50         /// <returns></returns>
     51         public XmlDocument QuerySoapWebService(String URL, String MethodName, Hashtable Pars, string soapVersion)
     52         {
     53             if (_xmlNamespaces.ContainsKey(URL))
     54             {
     55                 return QuerySoapWebService(URL, MethodName, Pars, _xmlNamespaces[URL].ToString(), soapVersion);
     56             }
     57             else
     58             {
     59                 return QuerySoapWebService(URL, MethodName, Pars, GetNamespace(URL), soapVersion);
     60             }
     61         }
     62         XmlDocument doc = null;
     63         private XmlDocument QuerySoapWebService(String URL, String MethodName, Hashtable Pars, string XmlNs, string soapVersion)
     64         {
     65             _xmlNamespaces[URL] = XmlNs;//加入缓存,提高效率
     66             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
     67             request.Method = "POST";
     68 
     69             if (soapVersion == "1.2")
     70             {
     71                 request.ContentType = "application/soap+xml;charset=utf-8";
     72             }
     73             else
     74             {
     75                 request.ContentType = "text/xml; charset=utf-8";
     76                 request.Headers.Add("SOAPAction", """ + XmlNs + (XmlNs.EndsWith("/") ? "" : "/") + MethodName + """);
     77             }
     78             SetWebRequest(request);
     79             byte[] data = EncodeParsToSoap(Pars, XmlNs, MethodName, soapVersion);
     80             WriteRequestData(request, data);
     81 
     82             XmlDocument doc2 = new XmlDocument();
     83             IAsyncResult ar = request.BeginGetResponse(AsyncCallbackGetResponse, request);//异步方式
     84             //XmlDocument doc = new XmlDocument();
     85             //doc = ReadXmlResponse(request.GetResponse());//原来同步方式
     86             if (ar.IsCompleted && doc != null)
     87             {
     88                 XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
     89                 String RetXml = "";
     90                 if (soapVersion == "1.2")
     91                 {
     92                     mgr.AddNamespace("soap12", "http://www.w3.org/2003/05/soap-envelope");
     93                     RetXml = doc.SelectSingleNode("//soap12:Body/*/*", mgr).InnerXml;
     94                 }
     95                 else
     96                 {
     97                     mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
     98                     RetXml = doc.SelectSingleNode("//soap:Body/*/*", mgr).InnerXml;
     99                 }
    100                 doc2.LoadXml("<root>" + RetXml + "</root>");
    101                 AddDelaration(doc2);
    102 
    103             }
    104             return doc2;
    105         }
    106 
    107         private void AsyncCallbackGetResponse(IAsyncResult ar)
    108         {
    109             WebRequest request = ar.AsyncState as WebRequest;
    110             var response = request.EndGetResponse(ar);
    111             Stream stream = response.GetResponseStream();
    112             using (StreamReader reader = new StreamReader(stream))
    113             {
    114                 string content = reader.ReadToEnd();
    115                 if (!string.IsNullOrEmpty(content))
    116                 {
    117                     doc = new XmlDocument();
    118                     doc.LoadXml(content);
    119                 }
    120             }
    121         }
    122 
    123         /// <summary>
    124         /// 第一次执行获取要访问的命名空间
    125         /// </summary>
    126         /// <param name="URL"></param>
    127         /// <returns></returns>
    128         private string GetNamespace(String URL)
    129         {
    130             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL + "?WSDL");
    131             SetWebRequest(request);
    132             WebResponse response = request.GetResponse();
    133             XmlDocument doc = null;
    134             using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
    135             {
    136                 doc = new XmlDocument();
    137                 doc.LoadXml(sr.ReadToEnd());
    138                 sr.Close();
    139             }
    140             return doc != null ? doc.SelectSingleNode("//@targetNamespace").Value : "http://tempuri.org/";
    141         }
    142 
    143         private byte[] EncodeParsToSoap(Hashtable Pars, String XmlNs, String MethodName, string soapVersion)
    144         {
    145             XmlDocument doc = new XmlDocument();
    146             XmlElement soapBody = null;
    147             if (soapVersion == "1.2")
    148             {
    149                 doc.LoadXml("<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"></soap12:Envelope>");
    150                 AddDelaration(doc);
    151                 soapBody = doc.CreateElement("soap12", "Body", "http://www.w3.org/2003/05/soap-envelope");
    152             }
    153             else
    154             {
    155                 doc.LoadXml("<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"></soap:Envelope>");
    156                 AddDelaration(doc);
    157                 soapBody = doc.CreateElement("soap", "Body", "http://schemas.xmlsoap.org/soap/envelope/");
    158             }
    159             XmlElement soapMethod = doc.CreateElement(MethodName);
    160             soapMethod.SetAttribute("xmlns", XmlNs);
    161             foreach (string k in Pars.Keys)
    162             {
    163                 XmlElement soapPar = doc.CreateElement(k);
    164                 soapPar.InnerXml = ObjectToSoapXml(Pars[k]);
    165                 soapMethod.AppendChild(soapPar);
    166             }
    167             soapBody.AppendChild(soapMethod);
    168             doc.DocumentElement.AppendChild(soapBody);
    169             return Encoding.UTF8.GetBytes(doc.OuterXml);
    170         }
    171 
    172         private string ObjectToSoapXml(object o)
    173         {
    174             XmlSerializer mySerializer = new XmlSerializer(o.GetType());
    175             MemoryStream ms = new MemoryStream();
    176             mySerializer.Serialize(ms, o);
    177             XmlDocument doc = new XmlDocument();
    178             doc.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));
    179             if (doc.DocumentElement != null)
    180             {
    181                 return doc.DocumentElement.InnerXml;
    182             }
    183             else
    184             {
    185                 return o.ToString();
    186             }
    187         }
    188 
    189         private void SetWebRequest(HttpWebRequest request)
    190         {
    191             request.Credentials = CredentialCache.DefaultCredentials;
    192             request.Timeout = 10000;
    193         }
    194 
    195         private void WriteRequestData(HttpWebRequest request, byte[] data)
    196         {
    197             request.ContentLength = data.Length;
    198             using (Stream writer = request.GetRequestStream())
    199             {
    200                 writer.Write(data, 0, data.Length);
    201                 writer.Close();
    202             }
    203         }
    204 
    205         private byte[] EncodePars(Hashtable Pars)
    206         {
    207             return Encoding.UTF8.GetBytes(ParsToString(Pars));
    208         }
    209 
    210         private String ParsToString(Hashtable Pars)
    211         {
    212             StringBuilder sb = new StringBuilder();
    213             foreach (string k in Pars.Keys)
    214             {
    215                 if (sb.Length > 0)
    216                 {
    217                     sb.Append("&");
    218                 }
    219                 sb.Append(HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(Pars[k].ToString()));
    220             }
    221             return sb.ToString();
    222         }
    223 
    224         private XmlDocument ReadXmlResponse(WebResponse response)
    225         {
    226             String retXml = "";
    227             using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
    228             {
    229                 retXml = sr.ReadToEnd();
    230                 sr.Close();
    231             }
    232             XmlDocument doc = new XmlDocument();
    233             if (!string.IsNullOrEmpty(retXml)) { doc.LoadXml(retXml); }
    234             return doc;
    235         }
    236 
    237         private void AddDelaration(XmlDocument doc)
    238         {
    239             XmlDeclaration decl = doc.CreateXmlDeclaration("1.0", "utf-8", null);
    240             doc.InsertBefore(decl, doc.DocumentElement);
    241         }
    242 
    243     }
    244 }
    View Code

      项目中的调用:

        WebServicesTool ws = new WebServicesTool();
        Hashtable paras = new Hashtable();
               paras.Add("ss", "你好");
               XmlDocument docPost = ws.QueryPostWebService("http://172.168.0.40:8086/WebSer.asmx", "HelloWorld", paras);
               XmlDocument docSoap = ws.QuerySoapWebService("http://172.168.0.40:8086/WebSer.asmx", "HelloWorld", paras, "1.2");
               context.Response.Write(docPost.OuterXml);
               context.Response.Write(docSoap.OuterXml);

      3.3 使用wsdl代理类

        ①、查看webservice服务,访问Service Document,即这里的wsdl文件,

        ②、将查看的wsdl文件保存。

        ③、本机使用VS自带的“VS2013 开发人员命令提示”。点击打开

        ④、wsdl  文件路径,即可生成代理类,调用代理类即可访问不同程序编写的webservice服务

    四、心得

      4.1 使用代码方式,使得成熟稳定的项目,易于扩展。

      4.2 不论何种语言,只要读懂wsdl及接口开发商提供文档即可,调用web接口。

      4.3 一些关于soap1.2 的简介http://blog.csdn.net/xiaojianpitt/article/details/5258254

        更多的soap介绍,可以查看:http://www.ibm.com/developerworks/cn/xml/x-sisoap/
        

  • 相关阅读:
    Longhorn,企业级云原生容器分布式存储
    Longhorn,企业级云原生容器分布式存储
    Longhorn,企业级云原生容器分布式存储
    数通——VLAN
    数通——动态路由
    AtCoder Beginner Contest 216 A~F 题解
    【YBTOJ】序列的第k个数
    【YBTOJ】【UVA10140】Prime Distance
    【YBTOJ】【CodeForces 372C】Watching Fireworks is Fun
    【YBTOJ】涂抹果酱
  • 原文地址:https://www.cnblogs.com/bjlhx/p/3547963.html
Copyright © 2011-2022 走看看