web-dev-qa-db-fra.com

Les propriétés de paramètre complexes de l'API Web sont toutes nulles

J'ai un appel de service API Web qui met à jour les préférences d'un utilisateur. Malheureusement, lorsque j'appelle cette méthode POST à partir d'un appel jQuery ajax, les propriétés de l'objet paramètre de requête sont toujours nulles (ou les valeurs par défaut), et non ce qui est transmis. Si j'appelle la même méthode exacte à l'aide d'un REST client (j'utilise Postman), cela fonctionne à merveille. Je ne peux pas comprendre ce que je fais de mal avec cela, mais j'espère que quelqu'un a déjà vu cela auparavant. C'est assez simple ...

Voici mon objet de requête: 

public class PreferenceRequest
{
    [Required]
    public int UserId;

    public bool usePopups;
    public bool useTheme;
    public int recentCount;
    public string[] detailsSections;
}

Voici ma méthode de contrôleur dans la classe UserController: 

    public HttpResponseMessage Post([FromBody]PreferenceRequest request)
    {
        if (request.systemsUserId > 0)
        {
            TheRepository.UpdateUserPreferences(request.UserId, request.usePopups, request.useTheme,
                                         request.recentCount, request.detailsSections);

            return Request.CreateResponse(HttpStatusCode.OK, "Preferences Updated");                
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, "You must provide User ID");
        }
    }

Voici mon appel ajax:

var request = {
    UserId: userId,
    usePopups: usePopups,
    useTheme: useTheme,
    recentCount: recentCount,
    detailsSections: details
};

$.ajax({
    type: "POST",
    data: request,
    url: "http://localhost:1111/service/User",
    success: function (data) {
        return callback(data);
    },
    error: function (error, statusText) {
        return callback(error);
    }
});

J'ai essayé de définir dataType & contentType sur plusieurs éléments différents ("json", "application/json", etc.), mais les propriétés de l'objet de requête sont toujours définies par défaut ou sont nulles. Donc, par exemple, si je passe dans cet objet:

var request = {
  UserId: 58576,
  usePopups: false,
  useTheme: true,
  recentCount: 10,
  detailsSections: ['addresses', 'aliases', 'arrests', 'events', 'classifications', 'custody', 'identifiers', 'phone', 'remarks', 'watches']
}

Je peux voir un objet de requête entièrement rempli avec les valeurs valides énumérées ci-dessus Mais dans le contrôleur API Web, la demande est là, mais les propriétés sont les suivantes:

  UserId: 0,
  usePopups: false,
  useTheme: false,
  recentCount: 0,
  detailsSections: null

FYI - Je ne fais AUCUNE page ASP.Net MVC ou ASP.NET avec ce projet, j'utilise simplement l'API Web en tant que service et passe tous les appels à l'aide de jQuery $ .ajax.

Une idée de ce que je fais mal ici? Merci!

UPDATE: Je veux juste noter que j'ai plusieurs méthodes dans ce même projet d'API Web dans d'autres contrôleurs qui font cela exactement la même chose , et j'appelle le exactement de la même manière , et ils fonctionnent parfaitement! J'ai passé la matinée à comparer les différents appels, et il ne semble pas y avoir de différence entre la méthode et les en-têtes, mais cela ne fonctionne tout simplement pas avec cette méthode. 

J'ai également essayé de passer à une méthode Put, mais j'obtiens exactement les mêmes résultats: l'objet de requête entre, mais ne contient pas les valeurs correctes. Ce qui est tellement frustrant, c’est que j’ai environ 20 classes de contrôleurs dans ce projet, et les postes fonctionnent dans toutes ces ...

32
Eddie

Cela semble être un problème commun en ce qui concerne Asp.Net WebAPI.
Généralement, la cause des objets nuls est la désérialisation de l'objet json dans l'objet C #. Malheureusement, il est très difficile de déboguer - et par conséquent de localiser votre problème.
Je préfère juste envoyer le JSON complet en tant qu’objet, puis le désérialiser manuellement. Au moins de cette façon, vous obtenez de vraies erreurs au lieu de NULL.
Si vous modifiez la signature de votre méthode pour accepter un objet, utilisez JsonConvert:

public HttpResponseMessage Post(Object model)
        {
            var jsonString = model.ToString();
            PreferenceRequest result = JsonConvert.DeserializeObject<PreferenceRequest>(jsonString);
        }
50
blorkfish

Peut-être que ça va aider, j'avais le même problème. 

Tout fonctionnait bien et, soudainement, toutes les propriétés étaient en défaut. 

Après un rapide test, je ai constaté que c'était le [Serializable] qui posait problème:

public IHttpActionResult Post(MyComplexClass myTaskObject)
{
    //MyTaskObject is not null, but every member are (the constructor get called).
}

et voici un extrait de ma classe: 

[Serializable]  <-- have to remove that [if it was added for any reason..]
public class MyComplexClass()
{
     public MyComplexClass ()
     {
        ..initiate my variables..
     }

     public string blabla {get;set;}
     public int intTest {get;set;
}
9
Simon

En utilisant cette technique publiée par @blorkfish a très bien fonctionné:

public HttpResponseMessage Post(Object model)
    {
        var jsonString = model.ToString();
        PreferenceRequest result = JsonConvert.DeserializeObject<PreferenceRequest>(jsonString);
    }

J'avais un objet paramètre, qui avait deux types natifs et deux autres objets en tant que propriétés. Les objets avaient des constructeurs marqués interne et l'appel JsonConvert.DeserializedObject sur le jsonString donnait l'erreur: 

Impossible de trouver un constructeur à utiliser pour le type ChildObjectB. Une classe devrait soit avoir un constructeur par défaut, un constructeur avec arguments ou un constructeur marqué avec l'attribut JsonConstructor.

J'ai changé les constructeurs en public et il remplit maintenant l'objet de paramètre lorsque je remplace le paramètre de modèle Object par l'objet réel. Merci!

4
Russell D

Je suppose que le problème est que votre contrôleur attend le type de contenu [FromBody], essayez de modifier votre contrôleur en 

public HttpResponseMessage Post(PreferenceRequest request)

et ajax à 

$.ajax({
    type: "POST",
    data: JSON.stringify(request);,
    dataType: 'json',
    contentType: "application/json",
    url: "http://localhost:1111/service/User",

En passant, créer un modèle en javascript n'est peut-être pas la meilleure pratique.

4
Anshul Nigam

Donc, je suis conscient de 3 problèmes possibles où la valeur ne lie pas:

  1. non public sans paramètre ctor
  2. propriétés ne sont pas public settable
  3. Il y a une erreur de liaison entraînant un ModelState.Valid == false - les problèmes typiques sont: types de valeur non compatibles (objet json à chaîne, non-guid, etc.)

J'envisage donc si un filtre devrait être appliqué aux appels d'API qui renverrait une erreur si la liaison entraîne une erreur!

2
baHI

J'ai également été confronté à ce problème et après de nombreuses heures consacrées à debbug et à la recherche, le problème n'a pas été causé par des attributs Content-Type ou Type $ Ajax, mais par des valeurs NULL sur mon objet JSON. POST ni l'un ni l'autre ne modifie mais une fois les valeurs NULL corrigées, le POST était correct et est jeté de manière native dans ma classe respuestaComparacion, ici le code:

JSON

 respuestaComparacion: {
                            anioRegistro: NULL, --> cannot cast to BOOL
                            claveElector: NULL, --> cannot cast to BOOL
                            apellidoPaterno: true,
                            numeroEmisionCredencial: false,
                            nombre: true,
                            curp: NULL, --> cannot cast to BOOL
                            apellidoMaterno: true,
                            ocr: true
                        }

Manette

[Route("Similitud")]
[HttpPost]
[AllowAnonymous]

public IHttpActionResult SimilitudResult([FromBody] RespuestaComparacion req)
{
   var nombre = req.nombre;
}

Classe

public class RespuestaComparacion
    {
        public bool anioRegistro { get; set; }
        public bool claveElector { get; set; }
        public bool apellidoPaterno { get; set; }
        public bool numeroEmisionCredencial { get; set; }
        public bool nombre { get; set; }
        public bool curp { get; set; }
        public bool apellidoMaterno { get; set; }
        public bool ocr { get; set; }
    }

J'espère que cette aide.

2
Jorge Solano

Un peu tard pour le parti, mais j'avais le même problème et le correctif déclarait le contentType dans votre appel ajax

    var settings = {
        HelpText: $('#help-text').val(),
        BranchId: $("#branch-select").val(),
        Department: $('input[name=departmentRadios]:checked').val()

    };

$.ajax({
        url: 'http://localhost:25131/api/test/updatesettings',
        type: 'POST',
        data: JSON.stringify(settings),
        contentType: "application/json;charset=utf-8",
        success: function (data) {
            alert('Success');
        },
        error: function (x, y, z) {
            alert(x + '\n' + y + '\n' + z);
        }
    });

Et votre contrôleur API peut être configuré comme suit:

    [System.Web.Http.HttpPost]
    public IHttpActionResult UpdateSettings([FromBody()] UserSettings settings)
    {
//do stuff with your UserSettings object now
        return Ok("Successfully updated settings");
    }
2
Dan

Je sais, c'est un peu tard, mais je viens de rencontrer le même problème. La réponse de @ blorkfish a également fonctionné pour moi, mais m'a amené à une solution différente. L'une des parties de mon objet complexe manquait d'un constructeur sans paramètre. Après avoir ajouté ce constructeur, les deux demandes Put et Post ont commencé à fonctionner comme prévu.

2
Storm

Je suis tombé sur le même problème. Le correctif nécessaire était de s'assurer que toutes les propriétés pouvant être sérialisées pour votre classe de paramètres JSON étaient bien obtenues; ensemble; méthodes explicitement définies. Ne comptez pas sur la syntaxe de la propriété auto C #! J'espère que cela sera corrigé dans les versions ultérieures de asp.net.

1
Rams Ramanathan

J'ai rencontré le même problème, la solution pour moi était de faire en sorte que les types d'attributs de ma classe correspondent aux attributs JSON, je veux dire

Json: "attribute": "true"

Devrait être traité comme une chaîne et non pas comme un booléen, comme si vous aviez un problème comme celui-ci, tous les attributs situés sous l'attribut défectueux auront la valeur null

0
Igor Vargas

Mon problème n’a été résolu par aucune des autres réponses. Cette solution mérite donc d’être examinée:

J'ai eu le DTO et la méthode de contrôleur suivants:

public class ProjectDetailedOverviewDto
{
    public int PropertyPlanId { get; set; }

    public int ProjectId { get; set; }

    public string DetailedOverview { get; set; }
}

public JsonNetResult SaveDetailedOverview(ProjectDetailedOverviewDto detailedOverview) { ... }

Parce que mon DTO avait une propriété portant le même nom que le paramètre (detailedOverview), le désérialisateur a été dérouté et essayait de renseigner le paramètre avec la chaîne plutôt qu'avec l'objet complexe entier Paramètre de la méthode du contrôleur à 'overview' pour que le désérialiseur sache que je n'essayais pas d'accéder à la propriété.

0
Delorian

Je fais face à ce problème cela me le répare 

  1. use attribut [JsonProperty ("nom de la propriété comme dans la requête json")]
  2. si vous sérialisez appelez uniquement PostAsync

comme ça

var json = JsonConvert.SerializeObject(yourobject);
var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var response = await client.PostAsync ("apiURL", stringContent);
  1. si le travail n'est pas retiré de votre api
0
Mostafa Elzaref

J'ai rencontré le même problème aujourd'hui aussi. Après avoir essayé toutes ces solutions, débogué l'API d'Azure et l'application Xamarin Android, il s'avère qu'il s'agissait d'un problème de mise à jour de référence. N'oubliez pas de vous assurer que le package Newtonsoft.JSON NUGET a été mis à jour dans votre API (si vous l'utilisez). 

0
SHOTdeJAGER

Aujourd'hui, j'ai le même problème que le vôtre. Lorsque j'envoie une demande POST à partir de ajax, le contrôleur reçoit un objet vide avec Null et les valeurs de propriété par défaut . La méthode est la suivante:

[HttpPost]
public async Task<IActionResult> SaveDrawing([FromBody]DrawingModel drawing)
{


    try
    {

        await Task.Factory.StartNew(() =>
        {
            //Logic
        });
        return Ok();
    }
    catch(Exception e)
    {
        return BadRequest();
    }
}

Mon Content-Type était correct et tout le reste était également correct. Après avoir essayé beaucoup de choses, j'ai trouvé que l'envoi de l'objet ressemblait à ceci:

$.ajax({
    url: '/DrawingBoard/SaveDrawing',
    type: 'POST',
    contentType: 'application/json',
    data: dataToPost
}).done((response) => { }); 

Cela ne fonctionnera pas, mais l'envoyer comme ceci à la place a fonctionné:

$.ajax({
    url: '/DrawingBoard/SaveDrawing',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify(dataToPost)
}).done((response) => { }); 

Oui, la JSON.stringify() manquante a causé tout le problème et il n'est pas nécessaire de mettre = ou autre chose comme préfixe ou suffixe à JSON.stringify. Après avoir creusé un petit peu. J'ai trouvé que le format de la charge utile de la demande était complètement différent entre les deux demandes
Ceci est la charge de la requête avec JSON.stringify

 Request payload with JSON.stringify

Et ceci est la charge utile de la demande sans JSON.stringify

 Request payload without JSON.stringify

Et n'oubliez pas, lorsque les choses deviennent magiques et que vous utilisez Google Chrome pour tester votre application Web. Vider le cache et recharger de temps en temps.

0
Mahmoud

Comme cette question me troublait presque une journée de travail hier, je souhaite ajouter quelque chose qui pourrait aider d'autres personnes dans la même situation.

J'ai utilisé Xamarin Studio pour créer mon projet d'api angulaire et Web. Pendant le débogage, l’objet passait parfois par null et parfois par un objet peuplé. Ce n'est que lorsque j'ai commencé à déboguer mon projet dans Visual Studio que mon objet a été renseigné à chaque demande de publication. Cela semble poser un problème lors du débogage dans Xamarin Studio.

Essayez de déboguer dans Visual Studio si vous rencontrez ce problème de paramètre null avec un autre IDE.

0
tno2007