Запрос об обрезке. До достижения метода действия, для привязки модели в XML - PullRequest
0 голосов
/ 13 декабря 2018

У меня есть интерфейс Ethernet-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; }
     }
 }

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Тело запроса указывает, что встроенное программное обеспечение публикует многокомпонентные данные.И следующее content-disposition означает, что он отправляет файл details.xml:

------------------------------3cbec9ce8f05
Content-Disposition: form-data; name="ServerData"; filename="details.xml"
Content-Type: text/plain

Так что вам не нужно вручную удалять границу '------------------------------ 3cbec9ce8f05 'и Content-Type=.... Просто используйте Request.Form.Files.

Кроме того, как рекомендует @ ivan-valadares , вы можете использовать Модельный переплет для подъема тяжелых предметов.Но похоже, что он обрабатывает все тело запроса как строку, а затем создает XDocument.Гораздо более элегантный способ - использовать XmlSerializer для создания строго типизированного объекта.Кроме того, Интерфейс IModelBinder не имеет метода public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext).Вместо этого мы должны использовать BindModelAsync(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;
    }
}

Теперь вы можете использовать ее в методе Action:

    [HttpPost]
    public IActionResult Post([ModelBinder(typeof(EmbededServerDataBinder<Ow_ServerModel>))] Ow_ServerModel ServerData)
    {
        return Ok("Working");
    }

Обратите внимание, имя ServerData имеет значение.Механизм связывания будет искать это имя в расположении содержимого.

Я проверяю его с вашей полезной нагрузкой, и оно работает для меня, как ожидалось:

enter image description here

0 голосов
/ 13 декабря 2018

Вы пытаетесь сделать это с помощью ActionFilter, я бы порекомендовал пользовательский Binder и использование регулярных выражений для извлечения XML из запроса.

Зарегистрируйте новый пользовательский Binder в WebApiConfig.cs

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...