4
votes

Comment puis-je améliorer ModelBinder dans ASP.NET Core (mettre à jour les valeurs de propriété pour une classe de modèle)

Je souhaite améliorer le résultat final renvoyé par ModelBinder .
Par exemple:

public class MyModel
{
    public int Order {get;set;}
    public string Title {get;set;}
}

Dans la méthode API, je m'attends à ce que toutes les propriétés de chaîne dans MyModel qui a MyUpperCaseAttribute soient en majuscules. P >

Par exemple:

public class MyModel
{
    public int Order {get;set;}
    [MyUpperCaseAttribute]
    [RemoveSpacesAttribute]
    public string Title {get;set;}
}

Mon idée était de remplacer ModelBinder par défaut et d'énumérer toutes les propriétés et de vérifier si la propriété est une chaîne et a MyUpperCaseAttribute et corrigez la valeur de la propriété en majuscules. Je vérifie la documentation, mais les exemples ne remplissent pas correctement, car ils redessinent complètement ce qui est retourné. Je voudrais simplement modifier les propriétés des résultats.

Quelle serait la meilleure option pour obtenir le comportement souhaité?

Important: (modifié):
Ce serait bien si les attributs de directive pouvaient être empilés:

[HttpPost("AddRecord")]
public async Task<ActionResult<int>> AddRecord(MyModel model)
{
    model.Title should be upper case, even if send from client in lower case.
}

Modifié:
Cela ressemble à ceci , mais sinon autre, c'est ASP.NET Core, et sur lien est juste ASP.NET. Méthode, propriétés, interfaces ... ne sont pas les mêmes.

Je devrais dire que ce serait bien si la règle de cas JSON fonctionnait:

public class MyModel
{
    public int Order {get;set;}

    [MyUpperCaseAttribute]
    public string Title {get;set;}
}

devrait fonctionner si {order: 1, title: "test"} (notez les minuscules) est envoyé depuis JavaScript.


7 commentaires

Copie possible du classeur de modèle de chaîne de soulignement


@IanKemp Ce n'est pas le cas. Il parle d'ASP.NET Core, pas d'ASP.NET. Il existe différentes méthodes, noms, interfaces, principes, implémentations ...


Il existe une pléthore de ressources sur la façon d'écrire des classeurs de modèles pour ASP.NET Core, vous devriez sûrement être en mesure de combiner l'un de ceux-ci avec la réponse à laquelle j'ai lié pour obtenir ce dont vous avez besoin?


La question du classeur de modèle de chaîne de soulignement explique comment lier une propriété. Je cherche comment traiter le modèle entier (modifier les valeurs des propriétés).


Le modèle entrant JSON est-il sérialisé?


@KirkLarkin Je ne sais pas ce que cela signifie, mais je pense que oui. J'utilise Angular 2+. Le type de contenu est content-type: application / json .


Ouais, c'est ce que je voulais dire. Dans ce cas, la désérialisation utilisera JSON.NET. Vous pourrez peut-être trouver quelque chose dans JSON.NET lui-même que vous pouvez configurer pour faire ce que vous recherchez.


3 Réponses :


0
votes

Vous pouvez faire cette chose dans votre MyUpperCaseAttribute comme suit:

public class MyUpperCaseAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if(value != null)
        {
            validationContext.ObjectType
            .GetProperty(validationContext.MemberName)
            .SetValue(validationContext.ObjectInstance, value.ToString().ToUpper(), null);
        }

        return null;
    }
}

La valeur de la propriété sera convertie en UpperCase pendant Reliure de modèle . Je l'ai vérifié de mon côté et cela fonctionne parfaitement.


5 commentaires

Je voudrais éviter cela. À l'heure actuelle, j'ai près de 100 appels d'API différents et j'aimerais éviter d'écrire ce code dans chaque méthode d'API. Et si j'utilise le même Model dans 2 méthodes API différentes. J'aurais besoin d'écrire ce code dans les deux méthodes. Avec ModelBinder sur un seul.


Mais j'ai toujours besoin d'appeler la méthode d'assistance dans chaque méthode API. Je voudrais éviter cela. Le développeur doit définir la classe de modèle, sur certains codes, il doit s'occuper de tout. Tout comme l'attribut Obligatoire . Vous n'écrivez pas de code dans chaque méthode API pour vérifier si toutes les propriétés Requis sont définies.


Je suis sûr que cela peut être fait automatiquement. Obligatoire, MaxLength ... toute la validation de modèle fonctionne hors de la boîte, cela devrait également. Je pense que cela a du sens. Le développeur doit modifier uniquement la classe de modèle. Rien d'autre.


Cette réponse est à mi-chemin, il suffit de l'empaqueter dans un ModelBinder personnalisé. J'ai lié à une question similaire qui fait tout, j'espère que c'est ce que vous recherchez.


Bonjour @TanvirArjel Je suis juste en train de vérifier JsonContractResolver et ceci depuis la mise à jour du modèle dans Validation sims un peu étrange. Sera de retour quand je recherche des choses.



1
votes

Ce n'est peut-être pas la "meilleure" option, mais j'utiliserais simplement la méthode d'extension .ToUpper () au lieu d'un filtre d'attribut personnalisé.

public class MyModel
{
    private string _title;
    public int Order {get;set;}

    public string Title { get => _title.ToUpper(); set => _title = value.ToUpper(); }
}


1 commentaires

C'est une idée, mais l'attribut serait beaucoup mieux (c'est plus court, plus transparent, cela devrait fonctionner hors de la boîte).



0
votes

Il y a un gros problème ici, et c'est le fait qu'il semble que c'est le genre de chose qui pourrait et devrait être accomplie via la liaison de modèles. Malheureusement, ce n'est pas le cas dans l'API Web ASP.Net Core: comme les données entrantes sont JSON, elles sont en fait gérées par formateurs d'entrée , pas des classeurs de modèles. Par conséquent, pour obtenir l'effet souhaité, vous devez créer votre propre formateur d'entrée personnalisé qui remplace le standard JsonInputFormatter .

D'abord l'attribut:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddMvc(options => options.UseToUppercaseJsonInputFormatter());
    }
}

Ensuite, nous décorez notre classe de modèle avec:

public static class MvcOptionsExtensions
{
    public static void UseToUppercaseJsonInputFormatter(this MvcOptions opts)
    {
        if (opts.InputFormatters.FirstOrDefault(f => f is JsonInputFormatter && !(f is JsonPatchInputFormatter)) is JsonInputFormatter jsonInputFormatter)
        {
            var jsonInputFormatterIndex = opts.InputFormatters.IndexOf(jsonInputFormatter);
            opts.InputFormatters[jsonInputFormatterIndex] = new ToUppercaseJsonInputFormatter(jsonInputFormatter);
        }
    }
}

Maintenant, créez notre formateur d'entrée personnalisé qui vérifie cet attribut et transforme la sortie si nécessaire. Dans ce cas, il encapsule et délègue simplement à JsonInputFormatter pour faire le gros du travail comme d'habitude, puis modifie le résultat s'il trouve notre attribut ToUppercaseAttribute sur une chaîne propriété:

public class ToUppercaseJsonInputFormatter : TextInputFormatter
{
    private readonly JsonInputFormatter _jsonInputFormatter;

    public ToUppercaseJsonInputFormatter(JsonInputFormatter jsonInputFormatter)
    {
        _jsonInputFormatter = jsonInputFormatter;

        foreach (var supportedEncoding in _jsonInputFormatter.SupportedEncodings)
            SupportedEncodings.Add(supportedEncoding);

        foreach (var supportedMediaType in _jsonInputFormatter.SupportedMediaTypes)
           SupportedMediaTypes.Add(supportedMediaType);
    }

    public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
    {
        var result = _jsonInputFormatter.ReadRequestBodyAsync(context, encoding);

        foreach (var property in context.ModelType.GetProperties().Where(p => p.PropertyType.IsAssignableFrom(typeof(string))
            && p.CustomAttributes.Any(a => a.AttributeType.IsAssignableFrom(typeof(ToUppercaseAttribute)))))
        {
            var value = (string)property.GetValue(result.Result.Model);
            property.SetValue(result.Result.Model, value.ToUpper());
        }

        return result;
    }
}

Ensuite, nous créons une méthode d'extension qui simplifie le remplacement du JsonInputFormatter par défaut par notre formateur personnalisé: p >

public class MyModel
{
    public int Order { get; set; }

    [ToUppercase]
    public string Title { get; set; }
}

Et enfin, appelez cette méthode pour effectuer le remplacement dans Startup.cs:

[AttributeUsage(AttributeTargets.Property)]
public class ToUppercaseAttribute : Attribute
{
}

Et voilÃ!


0 commentaires