1

尝试创建 Web API 服务以允许我的网站通过 javascript 访问外部 XML 提要(避免同源策略问题)。这些提要返回原始 XML,如下所示(使用 Fiddler 捕获):

要求:

GET http://wxdata.weather.com/wxdata/search/search?where=london HTTP/1.1
Host: wxdata.weather.com
Connection: Keep-Alive

回复:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=UTF-8
X-Varnish: 3773154810
X-Cache-Hits: 0
Cache-Control: max-age=55
Date: Wed, 27 Nov 2013 19:52:24 GMT
Content-Length: 504
Connection: keep-alive

<search ver="3.0">
          <loc id="UKXX0085" type="1">London, GLA, United Kingdom</loc><loc id="USAL2905" type="1">London, AL</loc><loc id="USAR0340" type="1">London, AR</loc><loc id="USCA9301" type="1">London, CA</loc><loc id="USIN2109" type="1">London, IN</loc><loc id="USKY1090" type="1">London, KY</loc><loc id="USMI2014" type="1">London, MI</loc><loc id="USMN2182" type="1">London, MN</loc><loc id="USMO2769" type="1">London, MO</loc><loc id="USOH0520" type="1">London, OH</loc>
        </search>

我希望使用 Web API 的内容协商功能。但是,鉴于响应的原始 XML 提要,我不确定控制器应该返回哪种类型。

public class SearchController : ApiController
{
    public string Get(string location)
    {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri("http://wxdata.weather.com");
        HttpResponseMessage resp = client.GetAsync(String.Format("wxdata/search/search?where={0}", location)).Result;
        resp.EnsureSuccessStatusCode();

    // Need help here

    }
}

我尝试了几种变体(通过 ReadAsStringAsync 的字符串、通过 LoadXml 的 XmlDocument、通过 JsonConvert.SerializeXmlNode 的字符串等)。到目前为止,我尝试过的所有方法都不适用于 Accept: application/xml 和 Accept: application/json 请求。一种接受类型有效,而另一种产生异常或未按要求格式化的结果。

我是 Web API 的新手。我所看到的一切似乎都建议将数据转换为适当的 CLR 类型,然后 Content Negotiation 应该负责其余的工作。只是不确定如何处理这个原始 XML 提要。我希望控制器按要求返回正确的 JSON 或正确的 XML(基本上通过原始提要中的原始 XML)。

有什么建议么?

4

1 回答 1

2

好吧,既然这个问题是风滚草,我会发布我自己找到的答案......

我发现这个(除其他外)有助于 .NET XML 序列化/反序列化的基础。我尝试使用xsd.exe,但结果令人失望。但是从这个基础开始,我为 XML 提要创建了一个 CLR 类型:

[XmlRoot("search")]
public class SearchTag
{
    [XmlAttribute("ver")]
    public string ver { get; set; }

    [XmlElement("loc")]
    public SearchLocTag[] loc { get; set; }
}

public class SearchLocTag
{
    [XmlAttribute("id")]
    public string id { get; set; }

    [XmlAttribute("type")]
    public string type { get; set; }

    [XmlText]
    public string text { get; set; }
}

XMLMediaTypeFormatter 使用的默认序列化程序 (DataContractSerializer) 对标记名称做了不希望的事情,并插入了不需要的 XmlSerializerNamespaces 信息。但我发现了这一点并创建了一个自定义 XmlObjectSerializer:

public class SearchSerializer : XmlObjectSerializer
{
    XmlSerializer serializer;

    public SearchSerializer()
    {
        this.serializer = new XmlSerializer(typeof(SearchTag));
    }

    public override void WriteObject(XmlDictionaryWriter writer, object graph)
    {
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("", "");
        serializer.Serialize(writer, graph, ns);
    }

    public override bool IsStartObject(XmlDictionaryReader reader)
    {
        throw new NotImplementedException();
    }

    public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
    {
        throw new NotImplementedException();
    }

    public override void WriteEndObject(XmlDictionaryWriter writer)
    {
        throw new NotImplementedException();
    }

    public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
    {
        throw new NotImplementedException();
    }

    public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
    {
        throw new NotImplementedException();
    }

}

然后将其添加到 Global.asax.cs:

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SetSerializer<SearchTag>(new SearchSerializer());

然后控制器变成了:

public class SearchController : ApiController
{
    public HttpResponseMessage Get(string location)
    {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri("http://wxdata.weather.com");
        HttpResponseMessage response = client.GetAsync(String.Format("wxdata/search/search?where={0}", location)).Result;
        response.EnsureSuccessStatusCode();

        var stream = response.Content.ReadAsStreamAsync().Result;

        var serializer = new XmlSerializer(typeof(SearchTag));
        using (var reader = new XmlTextReader(stream))
        {
            if (serializer.CanDeserialize(reader))
            {
                var xmlData = (SearchTag)serializer.Deserialize(reader);
                return Request.CreateResponse<SearchTag>(HttpStatusCode.OK, xmlData);
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
        }
    }
}

生成的应用程序/xml 消息:

要求

GET http://localhost:53047/twc/search/london HTTP/1.1
User-Agent: Fiddler
Accept: application/xml
Host: localhost:53047

回复

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcQ2xheXRvblxEb2N1bWVudHNcVmlzdWFsIFN0dWRpbyAyMDEzXFByb2plY3RzXFR3Y0FwaVx0d2Ncc2VhcmNoXGxvbmRvbg==?=
X-Powered-By: ASP.NET
Date: Wed, 04 Dec 2013 21:42:46 GMT
Content-Length: 484

<search ver="3.0"><loc id="UKXX0085" type="1">London, GLA, United Kingdom</loc><loc id="USAL2905" type="1">London, AL</loc><loc id="USAR0340" type="1">London, AR</loc><loc id="USCA9301" type="1">London, CA</loc><loc id="USIN2109" type="1">London, IN</loc><loc id="USKY1090" type="1">London, KY</loc><loc id="USMI2014" type="1">London, MI</loc><loc id="USMN2182" type="1">London, MN</loc><loc id="USMO2769" type="1">London, MO</loc><loc id="USOH0520" type="1">London, OH</loc></search>

以及由此产生的应用程序/json 消息:

要求

GET http://localhost:53047/twc/search/london HTTP/1.1
User-Agent: Fiddler
Accept: application/json
Host: localhost:53047

回复

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcQ2xheXRvblxEb2N1bWVudHNcVmlzdWFsIFN0dWRpbyAyMDEzXFByb2plY3RzXFR3Y0FwaVx0d2Ncc2VhcmNoXGxvbmRvbg==?=
X-Powered-By: ASP.NET
Date: Wed, 04 Dec 2013 21:43:18 GMT
Content-Length: 528

{"ver":"3.0","loc":[{"id":"UKXX0085","type":"1","text":"London, GLA, United Kingdom"},{"id":"USAL2905","type":"1","text":"London, AL"},{"id":"USAR0340","type":"1","text":"London, AR"},{"id":"USCA9301","type":"1","text":"London, CA"},{"id":"USIN2109","type":"1","text":"London, IN"},{"id":"USKY1090","type":"1","text":"London, KY"},{"id":"USMI2014","type":"1","text":"London, MI"},{"id":"USMN2182","type":"1","text":"London, MN"},{"id":"USMO2769","type":"1","text":"London, MO"},{"id":"USOH0520","type":"1","text":"London, OH"}]}

这正是我所追求的。

于 2013-12-04T22:06:58.803 回答