Почему мой HTTP-пост больше не передает содержимое тела после добавления Microsoft.AspNetCore.OData.Versioning - PullRequest
0 голосов
/ 15 марта 2019

Я работаю над ASP.NET Core 2.2 API, который реализует OData через Microsoft.AspNetCore.Odata v7.1.0 NuGet. У меня все работало нормально, поэтому я решил добавить управление версиями API через Microsoft.AspNetCore.OData.Versioning v3.1.0.

Теперь мои методы GET и GET {id} в моем контроллере корректно работают с версиями. Например, я могу добраться до метода конечной точки списка GET, используя URL

~/api/v1/addresscompliancecodes

или

~/api/addresscompliancecodes?api-version=1.0

Однако, когда я пытаюсь создать новую запись, запрос перенаправляется на правильный метод в контроллере, но теперь содержимое тела запроса не передается методу POST-контроллера

Я следовал примерам в Microsoft.ApsNetCore.OData.Versioning GitHub

В моем контроллере есть метод HttpPost;

    [HttpPost]
    [ODataRoute()]
    public async Task<IActionResult> CreateRecord([FromBody] AddressComplianceCode record, ODataQueryOptions<AddressComplianceCode> options)
    {

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Add(record);
        await _context.SaveChangesAsync();

        return Created(record);
    }

Когда я отлаживаю, запрос перенаправляется к методу контроллера правильно, но переменная «запись» теперь пуста, тогда как перед добавлением изменений кода для управления версиями API он был правильно заполнен.

Я подозреваю, что именно так я использую конструктор моделей, поскольку этот код был изменен для поддержки управления версиями API.

Прежде чем пытаться реализовать управление версиями API, я использовал класс построителя моделей, как показано ниже;

public class AddressComplianceCodeModelBuilder
{

    public IEdmModel GetEdmModel(IServiceProvider serviceProvider)
    {
        var builder = new ODataConventionModelBuilder(serviceProvider);

        builder.EntitySet<AddressComplianceCode>(nameof(AddressComplianceCode))
            .EntityType
            .Filter()
            .Count()
            .Expand()
            .OrderBy()
            .Page() // Allow for the $top and $skip Commands
            .Select(); 

        return builder.GetEdmModel();
    }

}

И метод Startup.cs -> Configure, как показано в приведенном ниже фрагменте кода;

        // Support for OData $batch
        app.UseODataBatching();

        app.UseMvc(routeBuilder =>
        {
            // Add support for OData to MVC pipeline
            routeBuilder
                .MapODataServiceRoute("ODataRoutes", "api/v1",
                    modelBuilder.GetEdmModel(app.ApplicationServices),
                    new DefaultODataBatchHandler());



        });

И он работал с [FromBody] в методе HttpPost контроллера.

Однако, следуя примерам в OData GitHub управления версиями API, я теперь использую класс конфигурации, подобный тому, что показан ниже, а не конструктор моделей из предыдущих;

public class AddressComplianceCodeModelConfiguration : IModelConfiguration
{

    private static readonly ApiVersion V1 = new ApiVersion(1, 0);

    private EntityTypeConfiguration<AddressComplianceCode> ConfigureCurrent(ODataModelBuilder builder)
    {
        var addressComplianceCode = builder.EntitySet<AddressComplianceCode>("AddressComplianceCodes").EntityType;

        addressComplianceCode
            .HasKey(p => p.Code)
            .Filter()
            .Count()
            .Expand()
            .OrderBy()
            .Page() // Allow for the $top and $skip Commands
            .Select();


        return addressComplianceCode;
    }
    public void Apply(ODataModelBuilder builder, ApiVersion apiVersion)
    {
        if (apiVersion == V1)
        {
            ConfigureCurrent(builder);
        }
    }
}

И мой метод Startup.cs -> Configure был изменен, как показано ниже;

    public void Configure(IApplicationBuilder app,
        IHostingEnvironment env, 
        VersionedODataModelBuilder modelBuilder)
    {

        // Support for OData $batch
        app.UseODataBatching();

        app.UseMvc(routeBuilder =>
        {
            // Add support for OData to MVC pipeline
            var models = modelBuilder.GetEdmModels();
            routeBuilder.MapVersionedODataRoutes("odata", "api", models);
            routeBuilder.MapVersionedODataRoutes("odata-bypath", "api/v{version:apiVersion}", models);
        });


    }

Если это уместно, у меня в Startup.cs есть следующий код -> ConfigureServices;

        // Add Microsoft's API versioning
        services.AddApiVersioning(options =>
        {
            // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
            options.ReportApiVersions = true;
        });

        // Add OData 4.0 Integration
        services.AddOData().EnableApiVersioning();

        services.AddMvc(options =>
            {
                options.EnableEndpointRouting = false; // TODO: Remove when OData does not causes exceptions anymore
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .AddJsonOptions(opt =>
            {
                opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });

Мне кажется, проблема в том, что модель как-то не совпадает, но я не могу точно понять, почему это не так

ОБНОВЛЕНИЕ 3/18/19 - Дополнительная информация

Вот мой класс сущности;

[Table("AddressComplianceCodes")]
public class AddressComplianceCode : EntityBase
{
    [Key]
    [Column(TypeName = "char(2)")]
    [MaxLength(2)]
    public string Code { get; set; }

    [Required]
    [Column(TypeName = "varchar(150)")]
    [MaxLength(150)]
    public string Description { get; set; }
}

и класс EntityBase;

public class EntityBase : IEntityDate
{
    public bool MarkedForRetirement { get; set; }

    public DateTimeOffset? RetirementDate { get; set; }

    public DateTimeOffset? LastModifiedDate { get; set; }

    public string LastModifiedBy { get; set; }

    public DateTimeOffset? CreatedDate { get; set; }

    public string CreatedBy { get; set; }

    public bool Delete { get; set; }

    public bool Active { get; set; }
}

А вот тело запроса от Почтальона;

{   
    "@odata.context": "https://localhost:44331/api/v1/$metadata#AddressComplianceCodes",
    "Code": "Z1",
    "Description": "Test Label - This is a test for Z1",
    "Active": true
}

Есть идеи?

Ответы [ 2 ]

1 голос
/ 19 марта 2019

Как оказалось, проблема была в том, что я не использовал верблюжий случай в качестве имен своих свойств в теле запроса Postman. Это не было проблемой только с Microsoft.AspNetCore.Odata, но после добавления пакета NuGet для Microsoft.AspNetCore.Odata.Versioning произошел сбой с начальным символом в верхнем регистре имен свойств. Кажется, что Microsoft.AspNetCore.Odata.Versioning использует свой собственный MediaTypeFormatter, который позволяет использовать нижний регистр верблюда. Я обнаружил это в следующем посте GitHub; https://github.com/Microsoft/aspnet-api-versioning/issues/310

0 голосов
/ 25 апреля 2019

Там нет пользовательского MediaTypeFormatter, но поведение изменилось в 3.0, так как использование верблюжьей оболочки, по-видимому, по умолчанию для большинства основанных на JSON API.Однако это легко вернуть назад.

modelBuilder.ModelBuilderFactory = () => new ODataConventionModelBuilder();
// as opposed to the new default:
// modelBuilder.ModelBuilderFactory = () => new ODataConventionModelBuilder().EnableLowerCamelCase();

Это также место, где вы должны выполнить или изменить любую другую настройку, связанную со строителями моделей.Фабричный метод вызывается для создания нового построителя моделей для каждой версии API.

Стоит отметить, что вам , а не нужно дважды сопоставлять маршруты.Для демонстрации настроены по строке запроса и по URL-пути .Вы должны выбрать один или другой и удалить тот, который не используется.

Надеюсь, это поможет.

...