我正在为一系列微服务创建一个概念证明,目前我正致力于内容协商。
我在Jeremy Likness找到了一系列帖子,并开始查看他的demo显示与自定义格式化程序的内容协商。
我从GitHub克隆了该项目并运行了该项目。当我发送Postman和Insomnia的请求时,除了一个用例外,我得到了预期的结果。问题是我无法获得集合的CSV格式响应。
对于每个请求,我都会添加accept
标头和相应的值(text/csv
,text/xml
,text/json
)。
请求单个资源(http://localhost:5000/api/todo/1
)时,使用自定义格式化程序将响应格式化为CSV。这是预期的行为。但是,在请求集合(http://localhost:5000/api/todo
)时,响应的格式为JSON。
我错过了什么?我怎么做才能获得单个资源的CSV但是集合的JSON?包含accept: text/xml
和accept: text/json
的请求将返回accept标头中指定的内容。这只是text/csv
的一个问题。
我在Visual Studio 2017和VS Code中运行了相同结果的代码。
更新#1 - 我在CsvFormatter.WriteResponseBodyAsync()
上放置了一个断点。请求单个资源时,断点会受到影响。请求收集时,断点不会被击中。
这是startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true;
options.FormatterMappings.SetMediaTypeMappingForFormat("xml", MediaTypeHeaderValue.Parse("text/xml"));
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("text/json"));
options.FormatterMappings.SetMediaTypeMappingForFormat("csv", MediaTypeHeaderValue.Parse("text/csv"));
options.OutputFormatters.Add(new CsvFormatter());
})
.AddXmlSerializerFormatters();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
这是自定义格式化程序:
public class CsvFormatter : TextOutputFormatter
{
public CsvFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(System.Type type)
{
return type == typeof(TodoItem);
}
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var response = context.HttpContext.Response;
var buffer = new StringBuilder();
if (context.Object is IEnumerable<TodoItem>)
{
foreach(var todoItem in (IEnumerable<TodoItem>)context.Object)
{
FormatCsv(buffer, todoItem);
}
}
else
{
FormatCsv(buffer, (TodoItem)context.Object);
}
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
{
return writer.WriteAsync(buffer.ToString());
}
}
private static void FormatCsv(StringBuilder buffer, TodoItem item)
{
buffer.AppendLine($"{item.Id},\"{item.Name}\",{item.IsComplete}");
}
}
答案 0 :(得分:1)
protected override bool CanWriteType(System.Type type)
{
return type == typeof(TodoItem);
}
显然,这会使您的格式化程序仅在返回类型为TodoItem
的实体时运行。如果您希望格式化程序仅针对TodoItem
的集合运行,则必须在此处进行检查。
根据您要返回的收集类型,您必须确保检查返回true
。一种方法是检查类型是否可分配给IEnumerable<TodoItem>
:
return typeof(TodoItem).IsAssignableFrom(type) ||
typeof(IEnumerable<TodoItem>).IsAssignableFrom(type);
这样,您可以返回数组,列表和任何其他类型的可枚举。
顺便说一下。看看你的实现,你有没有理由使用StringBuilder
作为缓冲区?您可以直接写入输出流。