La validation et la Manipulation de paramètres d'entrée multi-locataire de l'API web

Supposons que nous avons un multi-locataire en application de blog. Chaque utilisateur de l'application peut avoir un certain nombre de blogs hébergés par le service.

Notre API permet la lecture et l'écriture de messages de blog. Dans certains cas, la spécification d'un BlogId est facultatif, par exemple, l'obtention de tous les posts tagged avec ASP.NET:

/api/posts?tags=aspnet

Si nous voulions afficher tous les articles taggés avec ASP.NET sur un spécifiques blog, nous pourrions nous demander:

/api/posts?blogId=10&tags=aspnet

Certaines méthodes de l'API de besoin valide BlogId, comme lors de la création d'un nouveau blog:

POST: /api/posts
{
    "blogid" : "10",
    "title" : "This is a blog post."
}

La BlogId doit être validé sur le serveur pour s'assurer qu'elle appartient au courant (authentifié) de l'utilisateur. Je tiens également à déduire par défaut de l'utilisateur blogId si elle n'est pas spécifié dans la demande (pour plus de simplicité vous pouvez supposer que la valeur par défaut est l'utilisateur du premier blog).

Nous avons une IAccountContext objet qui contient des informations sur l'utilisateur actuel. Cela peut être injecté si nécessaire.

{
    bool ValidateBlogId(int blogId);
    string GetDefaultBlog();
}

Dans ASP.NET Web API quelle serait l'approche recommandée pour:

  1. Si le BlogId est indiqué dans le corps du message ou d'uri, de les valider pour s'assurer qu'il appartient à l'utilisateur actuel. Jeter une erreur 400 si ce n'.
  2. Si le BlogId n'est pas spécifié dans la demande, de récupérer la valeur par défaut BlogId de IAccountContext et de mettre à la disposition du contrôleur de l'action. Je ne veux pas que le contrôleur soit au courant de cette logique, c'est pourquoi je ne veux pas l'appeler IAccountContext directement à partir de mon action.

[Mise à jour]

À la suite de discussions sur Twitter, et en tenant compte @Aliostad les conseils, j'ai décidé de traiter le Blog en tant que ressource et de faire partie de mon modèle Uri (il est donc toujours nécessaire) c'est à dire

GET api/blog/1/posts -- get all posts for blog 1
PUT api/blog/1/posts/5 -- update post 5 in blog 1

Ma logique de requête pour le chargement des éléments uniques a été mis à jour à la charge par la Poste id et le blog id (pour éviter les locataires de chargement/mise à jour des autres peuples postes).

La seule chose à faire était de valider la BlogId. C'est une honte que nous ne pouvons pas utiliser les attributs de validation sur les paramètres Uri sinon @alexanderb recommandation aurait travaillé. Au lieu de cela j'ai opté pour l'utilisation d'un ActionFilter:

public class ValidateBlogAttribute : ActionFilterAttribute
{
    public IBlogValidator Validator { get; set; }

    public ValidateBlogAttribute()
    {
        //set up a fake validator for now
        Validator = new FakeBlogValidator();
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var blogId = actionContext.ActionArguments["blogId"] as int?;

        if (blogId.HasValue && !Validator.IsValidBlog(blogId.Value))
        {
            var message = new HttpResponseMessage(HttpStatusCode.BadRequest);
            message.ReasonPhrase = "Blog {0} does not belong to you.".FormatWith(blogId);
            throw new HttpResponseException(message);
        }

        base.OnActionExecuting(actionContext);
    }
}

public class FakeBlogValidator : IBlogValidator
{
    public bool IsValidBlog(int blogId)
    {
        return blogId != 999; //so we have something to test
    }
}

De la validation de la blogId est tout simplement un cas de décorer mon contrôleur/action avec [ValidateBlog].

Pratiquement tous les réponses ont aidé dans la solution, mais j'ai marqué comme @alexanderb est comme la réponse puisqu'il n'a pas couple la logique de validation à l'intérieur de mon contrôleur.

  • J'ai trouvé quelques incohérences dans cette question ou j'ai mal compris. Vous avez dit Each user of the application may have a number of blogs hosted by the service et en même temps If the BlogId is not specified in the request, retrieve the default BlogId from IAccountContext and make it available to the controller action. Si un utilisateur a une chance d'avoir plusieurs blogs à l'intérieur de ce service, comment voulez-u de récupérer un?
  • J'ai mis à jour ma question pour mieux expliquer. Si aucun blogId est spécifié, nous supposons que l'utilisateur par défaut du blog Id (dans la plupart des cas l'utilisateur aura seulement un blog).
InformationsquelleAutor Ben Foster | 2012-08-28