在MVC Core 2.1.3 API中使用FormatFilter属性

时间:2018-10-29 12:43:54

标签: c# asp.net-core-mvc autofac

[FormatFilter]在我的MVC Core 2.1.3 API中工作非常困难。 我希望端点支持JSON和XML,所以我编写了以下代码:

Startup类,该类继承自StartupCore类:

public class Startup : StartupCore
{
    protected override void OnConfigure(
        IApplicationBuilder app,
        IHostingEnvironment env,
        ILoggerFactory loggerFactory,
        IApplicationLifetime appLifetime) => AutoMappings.Initialize();
}

(部分)这个StartupCore

//....
services
    .AddCors()
    .AddMvcCore()
    .AddApiExplorer()
    .AddJsonFormatters()
    .AddXmlSerializerFormatters() //With or without this line; no luck
    .AddJsonOptions(options =>
    {
        options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
    })
    .AddMvcOptions(options =>
    {
        options.InputFormatters.Add(new PlainTextInputFormatter());
        options.OutputFormatters.Add(new CsvOutputFormatter());
        options.FormatterMappings.SetMediaTypeMappingForFormat("csv", MediaTypeHeaderValue.Parse("text/csv"));
        options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
        options.FormatterMappings.SetMediaTypeMappingForFormat("xml", MediaTypeHeaderValue.Parse("application/xml"));
    })
//.......

当我在控制器上使用FormatFilter属性时,例如

[HttpGet]
[FormatFilter]
[Route("/public/feed/{format}")]
public async Task<IActionResult> CreateFeed(string format)
{
    //
}

我收到错误消息: Autofac.Core.Registration.ComponentNotRegisteredException:所请求的服务“ Microsoft.AspNetCore.Mvc.Formatters.FormatFilter”尚未注册。

但是,当我使用Produces属性时,它会为我提供XML数据。

[HttpGet]
[Produces("application/xml")]
[Route("/public/feed/{format}")]
public async Task<IActionResult> CreateFeed(string format)
{
    //
}

我最终可能有两个端点;一个用于JSON,一个用于XML,但我宁愿使用FormatFilter作为一个终结点。

那么我在这里想念什么?

解决方法:目前,我正在使用Produces属性[Produces("application/json", "application/xml"]

使用的来源:https://andrewlock.net/formatting-response-data-as-xml-or-json-based-on-the-url-in-asp-net-core/

3 个答案:

答案 0 :(得分:2)

如果端点或控制器使用FormatFilter属性,则应通过AddFormatterMappings注册相应的服务:

package pl.test.rest;


import pl.test.model.Mestechnologygroup;
import pl.test.repo.TechnologyGroupRepo;

import javax.inject.Inject;
import javax.validation.constraints.Min;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;

import java.util.List;

import static javax.transaction.Transactional.TxType.REQUIRED;
import static javax.transaction.Transactional.TxType.SUPPORTS;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@Path("/tg")
public class TechnologyGroupEndpoint{

        @Inject
        private TechnologyGroupRepo technologyGroupRepo;

        @GET
        @Path("/{id : \\d+}")
        @Produces(APPLICATION_JSON)
        public Response getBook(@PathParam("id") @Min(1) Integer id) {
            Mestechnologygroup mestechnologygroup = technologyGroupRepo.find(id);

            if (mestechnologygroup == null)
                return Response.status(Response.Status.NOT_FOUND).build();

            return Response.ok(mestechnologygroup).build();
        }

        @DELETE
        @Path("/d/{id : \\d+}")
        public Response deleteBook(@PathParam("id") @Min(1) Integer id) {
            technologyGroupRepo.delete(id);
            return Response.noContent().build();
        }

        @GET
        @Produces(APPLICATION_JSON)
        public Response getBooks() {
            List<Mestechnologygroup> mestechnologygroups = technologyGroupRepo.findAll();

            if (mestechnologygroups.size() == 0)
                return Response.status(Response.Status.NO_CONTENT).build();

            return Response.ok(mestechnologygroups).build();
        }


}

答案 1 :(得分:1)

StartupCore中,您依靠OnMvcCoreServiceConfiguration事件添加XML输出格式化程序。这将在您呼叫AddMvcCore的线路上触发。然后,稍后,您再次调用AddMvcOptions,它将覆盖您之前的调用。在第二个调用中,您无需添加XML格式化程序,因此它实际上从未添加。

您需要注意发生的事情的异步性质。您正在传递一个将在应用程序启动时调用的操作,因此,第一次调用AddMvcOptions时,实际上还没有任何反应。当您以后再次调用它时,您将设置一个最终将要使用的新操作,以替换先前设置的操作。

答案 2 :(得分:0)

我的.Net Core 2.1 WebAPI项目也遇到了这个问题,发现我需要在Ajax调用中显式设置“接受”请求标头。在我最初的.Net 4.7 Web API项目中,将dataType属性设置为'json'或'xml'就足以返回正确的内容类型,但对于.Net Core而言似乎并不足够。我的Ajax通话如下:

$.ajax({
    type: 'GET',
    beforeSend: function (request)
    {
        request.setRequestHeader("Accept", "application/xml");
        // or for json request.setRequestHeader("Accept", "application/json");
    },
    traditional: true,
    url: uri,
    contentType: 'application/json; charset=utf-8',
    dataType: 'xml',
    // or for json dataType: 'json',
    success: function (data)
    {
        ...
    },
    error: function (req, status, err)
    {
        ...
    }
});

我还需要防止缓存,以确保每次通过在ResponseCache装饰器中设置NoStore属性来确保将正确的Accept标头传递给我的控制器方法:

    [ResponseCache(NoStore = true)]
    public ActionResult Get(string customerName)
    {
       ...
    }