Как предотвратить использование ReadAsStringAsync двукратно экранированной строки?

У меня есть метод Web API, который выглядит примерно так:

    [HttpPost]
    public ResponseMessageResult Post(Thing thing)
    {
        var content = "\r";
        var httpResponseMessage = Request.CreateResponse(HttpStatusCode.Accepted, content);
        return ResponseMessage(httpResponseMessage);
    }

В другом клиентском коде, когда я звоню:

    var content = httpResponseMessage.Content.ReadAsStringAsync().Result;

content:

    "\\r"

но я бы хотел, чтобы он оставался оригиналом:

    "\r"

почему клиент получает двукратную экранированную строку и как я могу предотвратить ее?

Ответы

Ответ 1

Он делает то, что делает, потому что вы взламываете яйцо кувалдой.

Когда вы вызываете Request.CreateResponse<string>(HttpStatusCode statusCode, T value) вы сообщаете веб-API, что вы хотите, чтобы ваше значение сериализовалось с использованием одного из Request.CreateResponse<string>(HttpStatusCode statusCode, T value) медиафайлов. Таким образом, веб-API наполняет ваше value в экземпляр объекта ObjectContent, что приводит к целому числу кодов conneg и определяет, что он может использовать Formatter X для сериализации вашего "объекта".

Скорее всего, именно JSONSerializer делает все возможное, чтобы попытаться вернуть вам строку, которую он считает нужным, а не символ CR.

В любом случае, вы можете сократить погоню и не выполнять 70 байонлий строк кода, используя объект HttpContent, который предназначен для отправки простых строк по проводу.

[HttpPost]
public ResponseMessageResult Post(Thing thing)
{
    var content = "\r";
    var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.Accepted) {
      RequestMessage = Request,
      Content = new StringContent(content)
    };
    return ResponseMessage(httpResponseMessage);
}

Ответ 2

Я знаю, что я, вероятно, собираюсь выпустить 70 байджонных строк кода, выполнив это (извините Даррела Миллера), но я обнаружил, что это было так же эффективно и менее разрушительно для моего выбранного шаблона разработки, чтобы использовать это:

response.Content.ReadAsAsync<string>().Result;

или

await response.Content.ReadAsAsync<string>();

вместо этого (что ускользает от котировок):

response.Content.ReadAsStringAsync().Result;

Примечание. ReadAsAsync является методом расширения в System.Net.Http.HttpContentExtensions в сборке System.Net.Http.Formatting. Если он недоступен в вашем проекте, вы можете добавить пакет NuGet Microsoft.AspNet.WebApi.Client.

Ответ 3

Вы не получаете значение @"\\r" назад, вы получаете "\\r" - вы не получите в своем ответе стенографического символа, потому что дословный символ - это просто команда, чтобы избежать строки определенным образом - сам модификатор verbatim не сохраняется как часть строки. Результатом является соответствующая экранированная версия того, к чему вы применили модификатор verbatim.

т.е. @"\r" дает вам строку "\\r" которая при применении к текстовому полю отображается как \r - экранированная обратная косая черта и 'r'.

Вам просто нужно снять модификатор слов с вашего первоначального назначения.

Это не имеет никакого отношения к ReadAsStringAsync - вы просто назначаете неверный строковый литерал в первую очередь.

Ответ 4

Если вы получаете буквальную двухсимвольный \r последовательность из ("\\r" в форме С#), то это почти наверняка, что вы ставите в. Вы говорите, что ваш веб - метод API "выглядит немного как это". Я сильно подозреваю, что проблема заключается в различии между тем, что вы опубликовали в своем вопросе, и тем, что находится в вашей реальной реализации.

Вам нужно проверить, что ваше ответное сообщение содержит фактические возвращения каретки, а не буквальный текст "\r". API-интерфейс чтения текста не будет искать буквенные escape-последовательности С# и обрабатывать их специально, потому что строки escape-последовательности строк С# не имеют смысла в текстовом виде. Если ваш текстовый файл содержит текст c:\name.txt, вы не ожидаете, что API чтения текста прочитает его как c:<NEWLINE>ame.txt.

Если вы хотите найти и преобразовать escape-последовательности С# -style, вам придется сделать это самостоятельно. Вы можете использовать такой метод (добавьте дополнительные escape-последовательности по мере необходимости):

private static string Unescape(string value) {
    if (value == null)
        return null;

    var length = value.Length;
    var result = new StringBuilder(length);

    for (var i = 0; i < length; i++) {
        var c = value[i];

        if (c == '\\' && i++ < length) {
            c = value[i];

            switch (c) {
                case 'n':
                    result.Append('\n');
                    break;
                case 'r':
                    result.Append('\r');
                    break;
                case 't':
                    result.Append('\t');
                    break;
                case '\\':
                    result.Append('\\');
                    break;
                default:
                    result.Append(c);
                    break;
            }
        }
        else {
            result.Append(c);
        }
    }

    return result.ToString();
}