لا يعتبر نموذج Asp.net core نموذجًا ملزمًا في حالة وجود قيم خاصية غير صالحة

2

أقوم بترحيل تطبيق من legap asp.net webapi إلى mc asp.net الأساسية. لقد لاحظت مشكلة. بالنسبة لبعض الطلبات ، نرسل قيمًا جزئية أو حتى غير صالحة في نص POST. و asp.net الأساسية ترفض إلغاء التسلسل.

مثل نموذج آخر

public class PostModel
{
    public int Id { get; set; }

    public Category? Category { get; set; }
}

public enum Category
{
    Public,
    Personal
}

عمل

[HttpPost]
public async Task<Response> Post([FromBody]PostModel model)
    => this.Service.Execute(model);

لطلب عينة التالية

POST /endpoint
{
    id: 3,
    category: "all"
}

ال ModelState مجموعة تسجل خطأ - تشير إلى أن all هي فئة غير صالحة ، و PostModel جدال model باطل. هل من الممكن تعطيل هذا السلوك ومحاولة فقط لربط جميع الخصائص الممكنة من هيئة البريد ، وتجاهل تلك التي لا يمكن ربطها؟ هذه هي الطريقة التي تم بها الأمر بالنسبة لنا في واجهة برمجة تطبيقاتنا القديمة وفي الوقت الحالي ، أحتاج إلى نقل هذا عبر.

تعطيل التحقق من صحة النموذج لم يساعدنا. ال model حجة لا تزال لاغية.

3 الاجابة

1
افضل جواب

إلى عن على FromBody ، فإنه سيتم ربط request body إلى Model بواسطة JsonInputFormatter .

إلى عن على JsonInputFormatter ، سوف تستدعي return InputFormatterResult.Success(model) عندما لا يكون هناك خطأ ، والاتصال return InputFormatterResult.Failure(); عندما يكون هناك أي خطأ. إلى عن على return InputFormatterResult.Failure(); ، لن يربط الخاصية الصالحة.

للحصول على حل ، يمكنك تنفيذ المنسق المخصص للعودة return InputFormatterResult.Success(model) .

  1. تنفيذ المنسق المخصص CustomFormatter استنادًا إلى JsonInputFormatter .
  2. يحل محل InputFormatterResult.Failure() مع InputFormatterResult.Success(model) .

                    if (!(exception is JsonException || exception is OverflowException))
                {
                    var exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception);
                    exceptionDispatchInfo.Throw();
                }
                return InputFormatterResult.Success(model);
    
  3. حقن CustomFormatter في Startup.cs

            services.AddMvc(o =>
        {
            var serviceProvider = services.BuildServiceProvider();
            var customJsonInputFormatter = new CustomFormatter(
                     serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<CustomFormatter>(),
                     serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
                     serviceProvider.GetRequiredService<ArrayPool<char>>(),
                     serviceProvider.GetRequiredService<ObjectPoolProvider>(),
                     o,
                     serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value
                );
    
            o.InputFormatters.Insert(0, customJsonInputFormatter);
        }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
:مؤلف
1
افضل جواب

في الواقع ، تتعلق مشكلتك بربط البيانات ، وليس بالتحقق ، ولهذا السبب لم يساعد تعطيل التحقق من صحة النموذج. يمكنك تنفيذ Binder المخصص وتكوينه لربط خصائصك يدويًا ، على سبيل المثال:

public class PostModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        string valueFromBody = string.Empty;

        using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body))
        {
            valueFromBody = sr.ReadToEnd();
        }

        if (string.IsNullOrEmpty(valueFromBody))
        {
            return Task.CompletedTask;
        }

        string idString = Convert.ToString(((JValue)JObject.Parse(valueFromBody)["id"]).Value);
        string categoryString = Convert.ToString(((JValue)JObject.Parse(valueFromBody)["category"]).Value);

        if (string.IsNullOrEmpty(idString) || !int.TryParse(idString, out int id))
        {
            return Task.CompletedTask;
        }

        Category? category = null;

        if(Enum.TryParse(categoryString, out Category parsedCategory))
        {
            category = parsedCategory;
        }

        bindingContext.Result = ModelBindingResult.Success(new PostModel()
        {
            Id = id,
            Category = category
        });

        return Task.CompletedTask;
    }
}

ثم يمكنك تطبيق هذا الموثق على فصلك:

[ModelBinder(BinderType = typeof(PostModelBinder))]
public class PostModel
{
    public int Id { get; set; }

    public Category? Category { get; set; }
}

أو للعمل:

[HttpPost]
public async Task<Response> Post([ModelBinder(BinderType = typeof(PostModelBinder))][FromBody]PostModel model)
    => this.Service.Execute(model);

أو إنشاء CustomModelBinderProvider:

public class CustomModelBinderProvider : IModelBinderProvider  
{  
    public IModelBinder GetBinder(ModelBinderProviderContext context)  
    {  
        if (context.Metadata.ModelType == typeof(PostModel))  
            return new PostModelBinder();  

        return null;  
    }  
}

وقم بتسجيله في أساليب ConfigureServices لفئة بدء التشغيل:

public void ConfigureServices(IServiceCollection services)  
{  
    ...
    services.AddMvc(  
        config => config.ModelBinderProviders.Insert(0, new CustomModelBinderProvider())  
    ); 
    ... 
} 
:مؤلف
0

لا لا يمكنك ذلك لأن العقار مرتبط بسرد. إذا كنت تريد حقًا أن تكون ما قمت بنشره ، فقم بتغيير النموذج ليكون

public class PostModel
{
    public int Id { get; set; }

    public string Category { get; set; }
}

ثم في نقطة النهاية ، قم بتحليل السلسلة لتعداد مثل

Enum.TryParse("All", out Category cat);
:مؤلف

أسئلة ذات صلة

فوق
قائمة طعام