#c# #csv #asp.net-core #formatter
Вопрос:
Я пытаюсь добавить поддержку CSV в конечные точки списка в моем REST API.
Однако у меня проблема в том, что мой пользовательский форматер вызывается только тогда, когда заголовок Accept является точным text/csv
. Это не сработает, если я добавлю кодировку или версию (API поддерживает управление версиями).
Так что ничего из этого не работает
text/csv; charset=utf-8
text/csv; v=1.0
text/csv; v=2.0
text/csv; charset=utf-8; v=1.0
text/csv; charset=utf-8; v=2.0
Однако пользователи API могут отправить кодировку и версию, поэтому это должно поддерживаться.
Я уже пытался:
- Вставьте форматер в первую очередь. Это приводит к тому, что он постоянно вызывается, хотя по умолчанию у меня должен быть json.
- Добавление полных заголовков с кодировкой и версией в
SupportedMediaTypes
. Это вообще не имеет никакого значения. - Добавление
text/csv; charset=utf-8; v=2.0
и так далее в[Produces]
атрибут метода контроллера. Это работает, но я просто не знаю всех возможных комбинаций во время компиляции, поэтому это невозможно.
Это мой класс
public class CsvOutputFormatter : TextOutputFormatter
{
public CsvOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
// I tried to add these explicitly, but it does not change anything.
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv; charset=utf-8"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv; charset=utf-8; v=1.0"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv; charset=utf-8; v=2.0"));
// UTF-8 is default, but all are supported if requested.
foreach (var encodingInfo in Encoding.GetEncodings())
{
var encoding = encodingInfo.GetEncoding();
if (encoding == Encoding.UTF8)
SupportedEncodings.Insert(0, encoding);
else
SupportedEncodings.Add(encoding);
}
}
public override IReadOnlyList<string> GetSupportedContentTypes(string contentType, System.Type objectType)
{
// This method only gets called during startup with either "application/hal json" or "text/csv".
// On my Controller method I have the attribute [Produces("application/hal json", "text/csv")]
return base.GetSupportedContentTypes(contentType, objectType);
}
protected override bool CanWriteType(System.Type type)
{
// This method only gets called if the Accept header is exactly "text/csv".
return typeof(Resource).IsAssignableFrom(type);
}
public override bool CanWriteResult(OutputFormatterCanWriteContext context)
{
// This method only gets called if the Accept header is exactly "text/csv" if the formatter is at the end of the list.
if (context.ContentType != "text/csv")
return false;
var resource = context.Object as Resource;
if (resource == null)
return false;
if (resource.Embedded == null)
return false;
if (!resource.Embedded.ContainsKey(Common.Constants.ListItems))
return false;
return true;
}
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
// This method only gets called if the Accept header is exactly "text/csv" if the formatter is at the end of the list.
write the csv...
}
}
Вот как это зарегистрировано в моем Startup
классе
services
.AddControllers(options =>
{
options.OutputFormatters.Add(new CsvOutputFormatter());
// Does not matter if it is true or false, the result is the same.
options.RespectBrowserAcceptHeader = true;
})
Из журналов видно, что ASP считает, что форматирование нормально только в том случае, если заголовок Accept точно text/csv
[09:08:40 DBG] List of registered output formatters, in the following order: ["Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.StringOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.StreamOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter", "RESTworld.AspNetCore.Formatter.CsvOutputFormatter"]
[09:08:40 DBG] Attempting to select an output formatter based on Accept header '["text/csv"]' and explicitly specified content types '["application/hal json", "text/csv"]'. The content types in the accept header must be a subset of the explicitly set content types.
[09:08:40 DBG] Selected output formatter 'RESTworld.AspNetCore.Formatter.CsvOutputFormatter' and content type 'text/csv' to write the response.
Если text/csv; charset=utf-8
это так, то он выбирает не мой форматер, а JSON по умолчанию.
[09:10:13 DBG] List of registered output formatters, in the following order: ["Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.StringOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.StreamOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter", "RESTworld.AspNetCore.Formatter.CsvOutputFormatter"]
[09:10:13 DBG] Attempting to select an output formatter based on Accept header '["text/csv; charset=utf-8"]' and explicitly specified content types '["application/hal json", "text/csv"]'. The content types in the accept header must be a subset of the explicitly set content types.
[09:10:13 DBG] Could not find an output formatter based on content negotiation. Accepted types were (["text/csv; charset=utf-8"])
[09:10:13 DBG] Attempting to select the first output formatter in the output formatters list which supports a content type from the explicitly specified content types '["application/hal json", "text/csv"]'.
[09:10:13 DBG] Selected output formatter 'Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter' and content type 'application/hal json' to write the response.
Комментарии:
1. Вы хотите получать много типов одновременно, а затем оценивать типы, чтобы давать разные заголовки приема?