Сериализация ProtoBuf через IHttpHandler дает неверные результаты

Я использую ASP.NET WebForms, что означает, что я буду отправлять данные Protobuf через страницы ASPX или ASHX.

Я пытаюсь создать файл GTFSRT, для которого есть пакет Nuget с именем GTFSRealTimeBindings. Это использует Protobuf и Protobuf-net для сжатия и отправки данных.

Проблема, с которой я сталкиваюсь, заключается в том, что при отправке данных что-то смешивается, поэтому их нельзя прочитать на принимающей стороне, и я не знаю, как это исправить. Я думаю, что дело в кодировке, но я ее не устанавливаю, поэтому не знаю, как ее изменить.

В итоге я написал обработчик http (страница ashx), который загрузит файл GTFS из другого источника, а затем просто попытается переслать его. Я знаю, что файл GTFS можно прочитать и декодировать из другого источника. Но каждый раз, когда я пытаюсь серверировать файл со своей страницы ashx, я не могу декодировать объект protbuf.

Вот очень простой набор кодов:

public class Vehicles : IHttpHandler
{

  public void ProcessRequest(HttpContext context)
  {

      WebRequest req = HttpWebRequest.Create("https://cdn.mbta.com/realtime/VehiclePositions.pb");
      FeedMessage feed = Serializer.Deserialize<FeedMessage>(req.GetResponse().GetResponseStream());

      Serializer.Serialize(context.Response.OutputStream, feed);
  }
}

В этом фрагменте вы заметите, что я загружаю файл Protobuf с cdn.mbta.com, а затем просто пытаюсь взять полученный результат и передать его обратно.

Когда я пытаюсь прочитать это в своем примере приложения:

WebRequest req = HttpWebRequest.Create("http://localhost:54988/Secure/Admin/Reports/GtfsRt/Vehicles.ashx");
FeedMessage feed = Serializer.Deserialize<FeedMessage>(req.GetResponse().GetResponseStream());

Я получаю сообщение: «Недопустимый тип провода; обычно это означает, что вы перезаписали файл без усечения или установки длины; см. http://stackoverflow.com/q/2152978/23354'

Если я запускаю Fiddler во время обращения к этой странице, я замечаю, что ответ, который я получаю от cdn.mbta.com, отличается от ответа, который дает эта страница (конечно, за вычетом заголовков).

Например, первые две строки с mbta.com в fiddler показывают:

2.0 ] y1601"T

Но первые две строчки моего ответа такие: 2.0 W y1601"N

Любые идеи о том, что вызывает это, и как я могу это исправить? Я попытался установить кодировку, используя

content.Response.ContentEncoding=Encoding.Utf8

Я и я перепробовали все остальные кодировки, чтобы попытаться установить его правильно.

======ОБНОВЛЕНИЕ====== В ответ на вопрос Марка я взял строку полезной нагрузки ответа в формате Base64, и ответ из первого источника не соответствует ответу после его пересылки.

Ответ от mbta.com (не более первых символов): Cg0KAzIumMBAAGI/e8eIFEl0KBXkwNzIzIlQKHAoIMzkyNTAwNjcqAjg4MAAaCDIwMTkwMjA3IABCDg

Ответ от моей службы (не более первых нескольких символов): CgsKAzIuMBiP3vHiBRJXCgV5MDcyMyJOChgKCDM5MjUwMDY3GggyMDE5MDIwNyoCODgSFA3skilCFQ

Как видите, они разные. Я буду работать над решением, которое я могу загрузить, чтобы продемонстрировать проблему. Еще раз спасибо!


person Ben Haynie    schedule 07.02.2019    source источник
comment
Я создал пример проекта, который может воспроизвести эту проблему. У него должна быть страница по умолчанию Vehicles.ashx, которая должна генерировать файл. Он находится здесь: dropbox.com/s/3fahjqk9ivz8m05/GTFSRT.zip?dl=0   -  person Ben Haynie    schedule 07.02.2019


Ответы (2)


Думаю, мне удалось найти решение.

Мой веб-код превратился в следующее:

    public void ProcessRequest(HttpContext context)
    {
        context.Response.Clear();
        context.Response.Buffer = true;
        context.Response.AddHeader("content-disposition", "attachment;filename=VehiclePositions.pb");
        context.Response.ContentType = "application/x-protobuf";
        WebRequest req = HttpWebRequest.Create("https://cdn.mbta.com/realtime/VehiclePositions.pb");
        FeedMessage feed = Serializer.Deserialize<FeedMessage>(req.GetResponse().GetResponseStream());
        using (MemoryStream ms = new MemoryStream())
        {
            Serializer.Serialize(ms, feed);
            context.Response.BinaryWrite(ms.ToArray());
            context.Response.End();
        }
    }

Не уверен, связано это или нет, но во время отладки я заметил, что иногда запрос не попадает в веб-службу. Удалена аутентификация, и теперь он может декодировать сообщение. Я считаю, что это работало раньше, когда я просматривал веб-код, но об этом нужно знать. Еще раз спасибо Марк!

person Ben Haynie    schedule 07.02.2019

То, как выглядит ответ в скрипаче, становится сложным, если вы не смотрите на двоичный код; лучше всего здесь посмотреть на вкладку HexView; все, что выделено зеленым, — это заголовки; все в черном это полезная нагрузка.

Используя ваш исходный URL-адрес https://cdn.mbta.com/realtime/VehiclePositions.pb, я отмечаю, что это ~ 26 КБ, но с 66591 байтом полезной нагрузки, с внутренним использованием gzip; поэтому вам нужно убедиться, что вы принимаете предложение Fiddler сначала декодировать это - затем я сделал «копию как base-64» в fiddler (только полезных данных, а не заголовков, на вкладке HexView) и прогнал его через https://protogen.marcgravell.com/decode (с использованием параметра base-64), и он проанализировал до конца.

Итак: я бы предложил:

  • сделайте аналогичный запрос вашему обработчику
  • проверь длину - она ​​66к? или 26к? или что-то другое? а если 26k, то помечен ли он как gzip-encoded?
  • когда вы копируете base-64 полезной нагрузки для каждого (при необходимости декодируется gzip): base-64 одинаков?
  • что https://protogen.marcgravell.com/decode говорит о base-64 от вашего обработчика ? принимает ли он его и разбирает до конца?

Это должно помочь вам определить проблему; или помогите мне идентифицировать его с вами.

person Marc Gravell    schedule 07.02.2019
comment
Марк, во-первых, спасибо! Я ценю вашу готовность помочь. - person Ben Haynie; 07.02.2019
comment
Кажется, я не могу добавлять новые строки в комментарии, поэтому я создам ответ на то, что я вижу в кодировке. Я также буду работать над созданием тестового проекта, которым я могу поделиться, чтобы показать проблему. Мне интересно, нужно ли мне кодировать ответ Base64, прежде чем отправлять его обратно по сети. - person Ben Haynie; 07.02.2019
comment
Я создал пример проекта, который может воспроизвести эту проблему. У него должна быть страница по умолчанию Vehicles.ashx, которая должна генерировать файл. Он находится здесь: dropbox.com/s/3fahjqk9ivz8m05/GTFSRT.zip. ?дл=0 - person Ben Haynie; 07.02.2019
comment
@BenHaynie Я скачал его; Я постараюсь посмотреть либо в эти выходные, либо во вторник - person Marc Gravell; 09.02.2019
comment
@BenHaynie пытается посмотреть на это сейчас (во вторник, в точку!) - в настоящее время борется с Не удалось найти часть пути {blah}\GTFSRT\GTFSRT\GTFSRT\bin\roslyn\csc.exe'. - надеюсь, я доберусь до интересных моментов! - person Marc Gravell; 12.02.2019
comment
@BenHaynie наконец-то заработал (после установки другой IDE; VS2019 ей доволен), и теперь я заметил, что вы ответили сами себе; значит ли это, что теперь у вас все в порядке? Я подозреваю, что на самом деле это был Response.End(), который имел значение, если это так! - person Marc Gravell; 12.02.2019
comment
да, я думаю, что я хороший. Спасибо большое за вашу помощь. Я надеюсь, что твоя простуда поправится! - person Ben Haynie; 13.02.2019
comment
@BenHaynie холодно? но: я рад, что вы все в порядке - person Marc Gravell; 14.02.2019