我有一个以太网到1-Wire接口,该接口定期发送带有传感器数据的HTTP帖子。数据主体使用XML,但它不是完全有效的XML。我无法更改HTTP正文,因为它位于嵌入式软件中。完整的请求正文如下所示:
------------------------------3cbec9ce8f05
Content-Disposition: form-data; name="ServerData"; filename="details.xml"
Content-Type: text/plain
<?xml version="1.0" encoding="UTF-8"?>
<Devices-Detail-Response xmlns="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PollCount>2739</PollCount>
<DevicesConnected>1</DevicesConnected>
<LoopTime>1.022</LoopTime>
<DevicesConnectedChannel1>0</DevicesConnectedChannel1>
<DevicesConnectedChannel2>0</DevicesConnectedChannel2>
<DevicesConnectedChannel3>1</DevicesConnectedChannel3>
<DataErrorsChannel1>0</DataErrorsChannel1>
<DataErrorsChannel2>0</DataErrorsChannel2>
<DataErrorsChannel3>0</DataErrorsChannel3>
<VoltageChannel1>4.91</VoltageChannel1>
<VoltageChannel2>4.92</VoltageChannel2>
<VoltageChannel3>4.92</VoltageChannel3>
<VoltagePower>5.16</VoltagePower>
<DeviceName>Unit 3 OW2</DeviceName>
<HostName>EDSOWSERVER2</HostName>
<MACAddress>00:00:00:00:00:00</MACAddress>
<DateTime>2018-12-12 16:44:48</DateTime>
<owd_DS18B20 Description="Programmable resolution thermometer">
<Name>DS18B20</Name>
<Family>28</Family>
<ROMId>F70000024D85E528</ROMId>
<Health>7</Health>
<Channel>3</Channel>
<RawData>C6004B467FFF0A102A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</RawData>
<PrimaryValue>12.3750 Deg C</PrimaryValue>
<Temperature Units="Centigrade">12.3750</Temperature>
<UserByte1 Writable="True">75</UserByte1>
<UserByte2 Writable="True">70</UserByte2>
<Resolution>12</Resolution>
<PowerSource>0</PowerSource>
</owd_DS18B20>
</Devices-Detail-Response>
------------------------------3cbec9ce8f05--
因此,我尝试先删除“ --------...”和Content-Type,最后删除“ ------- ..”,然后再执行操作方法。
这是我的控制器:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Monitor.Models;
using System;
using System.IO;
namespace Monitor.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class SensorController : Controller
{
private readonly ILogger _log;
public SensorController(ILogger<SensorController> logger)
{
_log = logger;
}
[HttpPost]
[OwServer]
public IActionResult Post([FromBody] Ow_ServerModel model)
{
return Ok("Working");
}
}
public class OwServer : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.HttpContext.Request.Headers["Content-Type"] = "application/xml";
using (StreamReader stream = new StreamReader(context.HttpContext.Request.Body))
{
string body = stream.ReadToEnd();
int start = body.IndexOf('<');
int last = body.LastIndexOf('>') + 1;
string parsedBody = body.Substring(start, (last - start));
// context.HttpContext.Request.Body =
}
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
}
using System;
using System.Xml.Serialization;
namespace Monitor.Models
{
[Serializable]
[XmlRoot("Devices-Detail-Response", Namespace = "http://www.example.com")]
public class Ow_ServerModel
{
public int PollCount { get; set; }
}
}
答案 0 :(得分:1)
您正尝试使用ActionFilter做到这一点,我建议使用自定义的活页夹,并使用正则表达式从请求中提取de xml。
在WebApiConfig.cs中注册新的自定义xml绑定器
public static void Register(HttpConfiguration config)
{
config.Services.Insert(typeof(ModelBinderProvider), 0,
new SimpleModelBinderProvider(typeof(XDocument), new XmlCustomBinder()));
}
创建一个将获取内容主体并仅提取xml的自定义活页夹
public class XmlCustomBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
try
{
var parsedXml = actionContext.Request.Content.ReadAsStringAsync().Result;
Regex regex = new Regex(@"<\?xml.*>", RegexOptions.Singleline);
Match match = regex.Match(parsedXml);
if (!match.Success) return false;
parsedXml = match.Groups[0].Value;
TextReader textReader = new StringReader(parsedXml);
XDocument xDocument = XDocument.Load(textReader);
bindingContext.Model = xDocument;
return true;
}
catch(Exception ex)
{
bindingContext.ModelState.AddModelError("XmlCustomBinder", ex);
return false;
}
}
}
控制器代码,获取XDocument(XML)值
[HttpPost]
[OwServer]
public IActionResult Post([ModelBinder(typeof(XmlCustomBinder))] XDocument xDocument)
{
return Ok("Working");
}
https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument?view=netframework-4.7.2
答案 1 :(得分:1)
请求正文指示嵌入式软件正在发布多部分数据。而以下content-disposition
表示它正在发送details.xml
的文件:
------------------------------3cbec9ce8f05
Content-Disposition: form-data; name="ServerData"; filename="details.xml"
Content-Type: text/plain
因此,您无需手动删除'------------------------------ 3cbec9ce8f05'和{ {1}}。 只需使用Content-Type=...
。
此外,根据@ivan-valadares的建议,您可以使用Model活页夹抬起沉重的东西。但是似乎他将所有请求正文都视为字符串,然后构造了Request.Form.Files
。一种更优雅的方法是使用XDocument
创建一个强类型的对象。另外,IModelBinder Interface没有方法XmlSerializer
。我们应该改用public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
。
因此,如下所示创建模型联编程序:
BindModelAsync(ModelBindingContext bindingContext)
现在您可以在Action方法中使用它:
public class EmbededServerDataBinder<T> : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); }
var modelName = bindingContext.BinderModelName ?? "ServerData";
XmlSerializer serializer = new XmlSerializer(typeof(T));
var data= bindingContext.HttpContext.Request.Form.Files[modelName];
if(data == null){
bindingContext.ModelState.AddModelError(modelName,"invalid error");
return Task.CompletedTask;
}
using(var stream = data.OpenReadStream()){
var o = serializer.Deserialize(stream);
bindingContext.Result = ModelBindingResult.Success(o);
}
return Task.CompletedTask;
}
}
请注意 [HttpPost]
public IActionResult Post([ModelBinder(typeof(EmbededServerDataBinder<Ow_ServerModel>))] Ow_ServerModel ServerData)
{
return Ok("Working");
}
的名称很重要。模型绑定器将在content-disposition中寻找该名称。
我用您的有效负载对其进行了测试,并且它对我来说符合预期: