C# 在到达操作方法之前修剪Request.Body,用于XML中的模型绑定
我有一个以太网到1线的接口,它定期发送一个带有传感器数据的HTTP post。数据体是XML格式的,只是它不是完全有效的XML。我无法更改HTTP主体,因为它位于嵌入式软件中。完整的请求正文如下所示:C# 在到达操作方法之前修剪Request.Body,用于XML中的模型绑定,c#,asp.net-core,asp.net-core-mvc,C#,Asp.net Core,Asp.net Core Mvc,我有一个以太网到1线的接口,它定期发送一个带有传感器数据的HTTP post。数据体是XML格式的,只是它不是完全有效的XML。我无法更改HTTP主体,因为它位于嵌入式软件中。完整的请求正文如下所示: ------------------------------3cbec9ce8f05 Content-Disposition: form-data; name="ServerData"; filename="details.xml" Content-Type: text/plain &l
------------------------------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--
------------------------------------3cbec9ce8f05
内容配置:表单数据;name=“ServerData”;filename=“details.xml”
内容类型:文本/纯文本
2739
1.
1.022
0
0
1.
0
0
0
4.91
4.92
4.92
5.16
第三单元OW2
EDSOWSERVER2
00:00:00:00:00:00
2018-12-12 16:44:48
DS18B20
28
F77000024D85E528
7.
3.
C6004B467FFF0A102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
12.3750摄氏度
12.3750
75
70
12
0
------------------------------3cbec9ce8f05--
因此,我试图在它到达action方法之前删除'----------…'和内容类型,以及结尾处的'-------…'
这是我的控制器:
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; }
}
}
使用Microsoft.AspNetCore.Mvc;
使用Microsoft.AspNetCore.Mvc.Filters;
使用Microsoft.Extensions.Logging;
使用Monitor.Models;
使用制度;
使用System.IO;
名称空间监视器.控制器
{
[ApiController]
[路由(“api/[控制器]”)]
公共类传感器控制器:控制器
{
私有只读ILogger_日志;
公共传感器控制器(ILogger记录器)
{
_日志=记录器;
}
[HttpPost]
[OwServer]
public IActionResult Post([FromBody]Ow\u服务器模型)
{
返回正常(“工作”);
}
}
公共类OwServer:Attribute,IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext上下文)
{
context.HttpContext.Request.Headers[“内容类型”]=“应用程序/xml”;
使用(StreamReader stream=newstreamreader(context.HttpContext.Request.Body))
{
字符串体=stream.ReadToEnd();
int start=body.IndexOf(“”)+1;
string parsedBody=body.Substring(start,(last-start));
//context.HttpContext.Request.Body=
}
}
public void OnResourceExecuted(ResourceExecutedContext上下文)
{
}
}
}
使用制度;
使用System.Xml.Serialization;
名称空间监视器.Models
{
[可序列化]
[XmlRoot(“设备详细信息响应”,命名空间=”http://www.example.com")]
公共类OWU服务器模型
{
公共整数PollCount{get;set;}
}
}
如果您试图使用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;
}
}
}
请求主体表示嵌入式软件正在发布多部分数据。下面的
内容配置
表示它正在发送一个details.xml文件
:
------------------------------3cbec9ce8f05
Content-Disposition: form-data; name="ServerData"; filename="details.xml"
Content-Type: text/plain
因此,您不需要手动删除'------------------------------------3cbec9ce8f05'和内容类型=…
的边界只需使用Request.Form.Files
此外,正如所建议的,您可以使用模型活页夹来提起重物。但他似乎将所有请求体都视为字符串,然后构造一个XDocument
。更优雅的方法是使用XmlSerializer
创建强类型对象。另外,没有public bool BindModel(HttpActionContext actionContext,ModelBindingContext bindingContext)的方法
。我们应该改用bindmodelsync(ModelBindingContext-bindingContext)
因此,创建一个模型绑定器,如下所示:
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;
}
}
公共类EmbeddedServerDataBinder:IModelBinder
{
公共任务BindModelAsync(ModelBindingContext bindingContext)
{
如果(bindingContext==null){抛出新的ArgumentNullException(nameof(bindingContext));}
var modelName=bindingContext.BinderModelName??“ServerData”;
XmlSerializer serializer=新的XmlSerializer(typeof(T));
var data=bindingContext.HttpContext.Request.Form.Files[modelName];
如果(数据==null){
bindingContext.ModelState.AddModelError(modelName,“无效错误”);
返回Task.CompletedTask;
}
使用(var stream=data.OpenReadStream()){
var o=序列化程序。反序列化(流);
bindingContext.Result=ModelBindingResult.Success(o);
}
返回Task.CompletedTask;
}
}
现在,您可以在操作方法中使用它:
[HttpPost]
public IActionResult Post([ModelBinder(typeof(EmbededServerDataBinder<Ow_ServerModel>))] Ow_ServerModel ServerData)
{
return Ok("Working");
}
[HttpPost]
公共IActionResult Post([ModelBinder(typeof(EmbeddedServerDataBinder))]Ow_服务器模型服务器数据)
{
返回正常(“工作”);
}
请注意ServerData
的名称。模型绑定器将在内容配置中查找此名称
我用你的有效载荷测试了它,它对我来说工作正常:
哇,谢谢!我不知道该文件在http请求中的外观如何,这会造成很多问题sense@itminus请注意:public bool BindModel(HttpActionContext actionContext,ModelBindingContext bindingContext)是WebApicController的modelbinder,BindModelAsync(ModelBindingContext bindingContext)是MVC控制器的modelbinder,因为[ApiController][Route(“api/[controller]”),我认为在本例中是webapi,但我没有看到扩展“:controller”而不是“:ApiController”。在这种情况下,不要忘记在global.asax中注册Modelbinder:ModelBinders.Binders.Add(typeof(XDocument),newxmlcustombinder())@IvanValadares您好,恐怕您没有注意到OP使用的是
ASP.NET核心
,而不是ASP.NET(经典版),WebApicController不存在
[HttpPost]
public IActionResult Post([ModelBinder(typeof(EmbededServerDataBinder<Ow_ServerModel>))] Ow_ServerModel ServerData)
{
return Ok("Working");
}