ASP Core,XML 反序列化,忽略 DTD DOCTYPE 标签(WorldPay 通知)
Posted
技术标签:
【中文标题】ASP Core,XML 反序列化,忽略 DTD DOCTYPE 标签(WorldPay 通知)【英文标题】:ASP Core, XML deserialization, ignore DTD DOCTYPE tag (WorldPay Notification) 【发布时间】:2021-08-20 04:02:48 【问题描述】:我正在尝试在 .NET Core 中编写一个 Web API 控制器来接收 Worldpay 通知。
我已经使用 XmlSerializerInputFormatter 完成了 AddMvc,它正在愉快地读取其有效负载的“调整”版本。
services.AddMvc(config =>
config.InputFormatters.Add(new XmlSerializerInputFormatter(config));
);
我的测试控制器
[HttpPost]
[Route("SendDocument")]
[Consumes("application/xml")]
public ActionResult SendDocument([FromBody] paymentService payment)
return Ok();
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE paymentService PUBLIC "-//Worldpay//DTD Worldpay PaymentService v1//EN"
"http://dtd.worldpay.com/paymentService_v1.dtd">
<paymentService version="1.4" merchantCode="Your_merchant_code">
<notify>
<orderStatusEvent orderCode="ExampleOrder1">
<payment>
<paymentMethod>ECMC-SSL</paymentMethod>
<amount value="2400" currencyCode="EUR" exponent="2" debitCreditIndicator="credit"/>
<lastEvent>AUTHORISED</lastEvent>
<AuthorisationId id="622206"/>
<balance accountType="IN_PROCESS_AUTHORISED">
<amount value="2400" currencyCode="EUR" exponent="2" debitCreditIndicator="credit"/>
</balance>
<cardNumber>5255********2490</cardNumber>
<riskScore value="0"/>
</payment>
</orderStatusEvent>
</notify>
</paymentService>
但是,它们在开始时包含一个 DOCTYPE 标记,如果我将其添加回有效负载中,它会失败。没有 DOCTYPE,它可以完美运行
我已经进行了大量搜索并查看了有关忽略 DTD 等的信息,但我不知道如何使用 InputFormatter 来做到这一点。
如何让 InputFormatter 忽略文件中的 DTD DOCTYPE 标记?
【问题讨论】:
也许您可以尝试使用jdweng
在link 中提到的自定义XmlSerializerInputFormatter。
【参考方案1】:
经过一番折腾,我得到了一些可能对其他人有用的东西
所以我的 ConfigureServices 包含
services.AddMvc(config =>
config.InputFormatters.Insert(0, new RawXmlRequestBodyFormatter());
);
我创建了一个原始格式化程序,它作为字符串通过 XML 并删除了任何以 /p> 开头的行
public class RawXmlRequestBodyFormatter : InputFormatter
public RawXmlRequestBodyFormatter()
SupportedMediaTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/xml"));
SupportedMediaTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/xml"));
/// <summary>
/// Allow text/xml, application/xml only
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Boolean CanRead(InputFormatterContext context)
if (context == null) throw new ArgumentNullException(nameof(context));
var contentType = context.HttpContext.Request.ContentType;
return (contentType == "text/xml" || contentType == "application/xml");
/// <summary>
/// Handle xml results
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
var contentType = context.HttpContext.Request.ContentType;
var httpContext = context.HttpContext;
if (contentType == "application/xml" || contentType == "text/xml")
using var reader = new StreamReader(httpContext.Request.Body, Encoding.UTF8);
StringBuilder xml = new StringBuilder();
try
while(true)
var Line = await ReadLineAsync(reader, context);
if (Line != null)
xml.AppendLine(Line);
else
break;
catch
return await InputFormatterResult.FailureAsync();
return await InputFormatterResult.SuccessAsync(xml.ToString());
return await InputFormatterResult.FailureAsync();
/// <summary>
/// Read next line, Skip any lines starting with DOCTYPE
/// </summary>
/// <param name="reader"></param>
/// <param name="context"></param>
/// <returns></returns>
private async Task<string> ReadLineAsync(StreamReader reader, InputFormatterContext context)
string line = null;
do
line = await reader.ReadLineAsync();
while (line != null && line.StartsWith("<!DOCTYPE"));
return line;
在我的控制器中,我像这样使用它。我获取 RAW XML 字符串,将其反序列化为我编写的 paymentService 类,然后以 JSON 格式返回它,以便您可以看到它在 POSTMAN 中工作
[HttpPost]
[Route("SendDocument")]
[Consumes("application/xml")]
public ActionResult SendDocument([FromBody] string raw)
try
XmlSerializer serializer = new XmlSerializer(typeof(paymentService), new XmlRootAttribute("paymentService"));
StringReader stringReader = new StringReader(raw);
var o = (paymentService)serializer.Deserialize(stringReader);
return Ok(o);
catch (Exception ex)
return Ok();
我的 paymentService 类是这些,为了完整性而提供的
public class amount
public amount()
[XmlAttribute]
public string value get; set;
[XmlAttribute]
public string currencyCode get; set;
[XmlAttribute]
public string exponent get; set;
[XmlAttribute]
public string debitCreditIndicator get; set;
public class balance
public balance()
[XmlAttribute]
public string accountType get; set;
[XmlElement]
public amount amount get; set;
public class riskScore
public riskScore()
[XmlAttribute]
public string value get; set;
public class authorisationId
public authorisationId()
[XmlAttribute]
public string id get; set;
public class payment
public payment()
[XmlElement]
public string paymentMethod get; set;
[XmlElement]
public amount amount get; set;
[XmlElement]
public string lastEvent get; set;
[XmlElement]
public authorisationId AuthorisationId get; set;
[XmlElement]
public balance balance get; set;
[XmlElement]
public string cardNumber get; set;
[XmlElement]
public riskScore riskScore get; set;
public class orderStatusEvent
public orderStatusEvent()
[XmlAttribute]
public string orderCode get; set;
[XmlElement]
public payment payment get; set;
public class notify
public notify()
[XmlElement]
public orderStatusEvent orderStatusEvent get; set;
[Serializable]
public class paymentService
public paymentService()
[XmlAttribute]
public string version get; set;
[XmlAttribute]
public string merchantCode get; set;
[XmlElement]
public notify notify get; set;
我希望这可以节省一些时间。 WorldPay 在提供信息方面毫无用处,而且要达到这一点需要数周时间。
【讨论】:
以上是关于ASP Core,XML 反序列化,忽略 DTD DOCTYPE 标签(WorldPay 通知)的主要内容,如果未能解决你的问题,请参考以下文章