2010-02-22 12 views
2

J'ai une méthode d'action comme ce qui suitComment puis-je passer deux paramètres de chaîne de requête différents qui représentent un paramètre de méthode d'action?

public JsonResult Index(string version) 
{ 
    .. do stuff, return some data v1 or v2. Default = v2. 
} 

Ainsi, cette méthode d'action retourne des données, qui peuvent être mises en forme soit sous forme Version 1 ou Version 2 (quelle que soit la sortie qui est ... Il suffit de savoir qu'ils sont schématiquement différent).

Ainsi, lorsqu'un utilisateur veut appeler l'accès à cette ressource, ils les éléments suivants:

http://www.blah.com/api/Index 

rien de trop dur.

ils peuvent aussi le faire ...

http://www.blah.com/api/Index?version=1.0 

MAIS, est-il possible de faire en sorte que l'utilisateur peut utiliser la chaîne de requête params version ou v

eg. http://www.blah.com/api/Index?v=1.0 

et cela remplissez le paramètre version dans la méthode ActionMethod. Possible?

+0

Oui vous ne pouvez pas utiliser les en-têtes directement si le navigateur si les utilisateurs veulent y naviguer manuellement à savoir le type dans l'URL dans la barre d'adresse, vous pouvez si vous faites toutes vos demandes si AJAX si - que je suppose que vous êtes si vous Vous renvoyez JSON? http://api.jquery.com/jQuery.ajax/ voir beforeSend pour ajouter l'en-tête. – Rosstified

+0

Vous pouvez bien sûr utiliser la même idée, mais basez-vous sur la chaîne de requête plutôt que sur l'en-tête, selon vos besoins pour l'API et son accessibilité. – Rosstified

Répondre

3

Je suppose que vous pourriez manipuler le (s) paramètre (s) de méthode d'action en utilisant un filtre d'action.

Fondamentalement juste vérifier un «v» dans la collection QueryString, et si elle existe, le jeter dans la collection ActionParameters.

public override void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    var version = filterContext.HttpContext.Request.QueryString["v"]; 
    if (!string.IsNullOrEmpty(version)) 
     filterContext.ActionParameters["version"] = version; 
} 

HTHS,
Charles

EDIT: Faire un peu plus générique ...

public class QueryStringToActionParamAttribute : ActionFilterAttribute 
{ 
    private string _queryStringName; 
    private string _actionParamName; 

    public QueryStringToActionParamAttribute(string queryStringName, string actionParamName) 
    { 
     _queryStringName = queryStringName; 
     _actionParamName = actionParamName; 
    } 

    public override void OnActionExecuting(ActionExecutedContext filterContext) 
    { 
     var queryStringValue = filterContext.HttpContext.Request.QueryString[_queryStringName]; 
     if (!string.IsNullOrEmpty(queryStringValue)) 
     { 
      filterContext.ActionParameters[_actionParamName] = queryStringValue; 
     } 
    } 
} 

Ensuite, vous pouvez l'appeler comme:

[QueryStringToActionParam("v", "version")]; 
+0

Idée de Goo. Je me demande s'il est possible de le rendre un peu plus générique et de passer le * nom * de la valeur de la chaîne de requête (c'est-à-dire 'v') puis le * nom * de l'actionparamater (ie version). Alors nous pourrions utiliser ce filtre d'action plusieurs fois et sur plusieurs méthodes d'action ..? –

+0

Bonne idée, voir mon édition. – Charlino

+0

sauce de génial. –

0

Un autre façon de gérer le versionnement de l'API est d'avoir réellement différentes versions de le contrôleur pour chaque version de l'API, de cette façon vous n'avez pas besoin d'avoir toutes les vérifications pour chaque numéro de version dans chaque méthode d'action. Chaque contrôleur est uniquement applicable à une version de l'API. Son nettoyeur (IMO) me gère le versionnement au moment de l'itinéraire plutôt que le temps d'action. Vous pouvez le faire avec une contrainte de routage pour vérifier le numéro de version.

Dans l'exemple ci-dessous, le V10 du contrôleur et V20 ne peut être mis en déroute à si la route contraint passé - à savoir l'en-tête était présent, si aucun en-tête par défaut (qui est v2) .:

routes.MapRoute(
      "EmployeeListingv1", 
      "employees", 
      new { controller = "V10Employees", action = "Index" }, // Parameter defaults 
      new { ApiV = new ApiVersionConstraint(ApiVersion.Version10) } 
    ); 

routes.MapRoute(
       "EmployeeListingv2", 
       "employees", 
       new { controller = "V20Employees", action = "Index" }, // Parameter defaults 
       new { ApiV = new ApiVersionConstraint(ApiVersion.Version20) } 
     ); 

Vous pouvez le faire en utilisant la chaîne de requête pour passer la version comme vous le faites actuellement et juste passer à une contrainte d'itinéraire, cependant j'ai trouvé plus facile à maintenir en utilisant un en-tête optionnel dans la requête. (c'est aussi plus "RESTful" sans entrer dans tout ce débat.) Pas d'en-tête signifie la version par défaut (dernière) de l'API.

API Exemple contrainte Version:

/// <summary> 
/// Enable routing of requests to controllers based on the 
/// API version contained in the header. 
/// </summary> 
public class ApiVersionConstraint : IRouteConstraint 
{ 
    const string VersionHeader = "X-MY-API-NAME-HERE-VERSION"; 

    private ApiVersion _version = ApiVersion.Unsupported; 

    public ApiVersionConstraint(ApiVersion version) 
    { 
     this._version = version; 
    } 

    #region IRouteConstraint Members 

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
    { 
     string vers = string.Empty; 

     if (httpContext.Request.Headers[VersionHeader] != null) 
     { 
      vers = httpContext.Request.Headers[VersionHeader]; 
     } 
     else 
     { 
      vers = "2.0"; // set default here. 
     } 

     ApiVersion fromHeader = ApiVersion.Unsupported; 

     switch (vers) 
     { 
      case "1.0": 
       { 
        fromHeader = ApiVersion.Version10; 
        break; 
       } 
      case "2.0": 
       { 
        fromHeader = ApiVersion.Version20; 
        break; 
       } 

      default: 
       { 
        fromHeader = ApiVersion.Unsupported; 
        break; 
       } 
     } 

     return fromHeader == _version; 

    } 

    #endregion 
} 
+0

c'est une idée assez kewl, en fait :) j'aime vraiment ça. Mais avec les valeurs d'en-tête, je ne peux pas simplement appeler l'API à partir d'un navigateur, cependant .. non? –

0

Juste pour ajouter quelque chose de plus à cette vieille question ... Vous pouvez combiner les techniques ici et appuyer si la sélection de version en-tête de requête ou une chaîne de requête en modifiant le chèque @ la réponse de Rosstified d'inclure un chèque QueryString:

 if (httpContext.Request.Headers[VersionHeader] != null) { 
      vers = httpContext.Request.Headers[VersionHeader]; 
     } else { 
      if (httpContext.Request.QueryString["v"] != null) { 
       vers = httpContext.Request.QueryString["v"]; 
      } else { 
       vers = "1.0"; // set default here. 
      } 
     }