Как я могу передать два разных параметра строки запроса, которые представляют один параметр метода действия?

у меня есть метод действия, подобный следующему

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

Таким образом, этот метод действия возвращает некоторые данные, которые могут быть отформатированы либо как Version 1, либо как Version 2 (каким бы ни был вывод... просто знайте, что они схематически различны).

Итак, когда пользователь хочет вызвать доступ к этому ресурсу, он делает следующее:

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

ничего сложного.

они и это умеют...

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

НО, возможно ли сделать так, чтобы пользователь мог использовать параметры строки запроса version или v

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

и это заполнит параметр версии в ActionMethod. Возможно?


person Pure.Krome    schedule 22.02.2010    source источник
comment
Да, вы не можете использовать заголовки напрямую через браузер, если пользователи хотят перемещаться туда вручную, т. Е. Введите URL-адрес в адресную строку, вы МОЖЕТЕ, если вы выполняете все свои запросы через AJAX, хотя я предполагаю, что вы это делаете, если возвращаете JSON? api.jquery.com/jQuery.ajax см. перед отправкой, чтобы добавить заголовок.   -  person Rosstified    schedule 23.02.2010
comment
Вы, конечно, можете использовать ту же идею, но основывать ее на строке запроса, а не на заголовке, в зависимости от ваших требований к API и его доступности.   -  person Rosstified    schedule 23.02.2010


Ответы (3)


Я предполагаю, что вы могли бы манипулировать параметрами метода действия, используя фильтр действия.

По сути, просто проверьте наличие 'v' в коллекции QueryString и, если она существует, добавьте ее в коллекцию ActionParameters.

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

HTHs,
Чарльз

РЕДАКТИРОВАТЬ: сделать его немного более общим...

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;
        }
    }
}

Тогда вы могли бы назвать это так:

[QueryStringToActionParam("v", "version")];
person Charlino    schedule 23.02.2010
comment
Идея. Интересно, можно ли сделать его немного более общим и передать имя значения строки запроса (т.е. 'v'), а затем имя параметра действия (т.е. версия). Таким образом, мы могли бы использовать этот фильтр действий несколько раз и в нескольких методах действий..? - person Pure.Krome; 23.02.2010

Альтернативный способ управления версиями API состоит в том, чтобы фактически иметь разные версии контроллера для каждой версии API, таким образом, вам не нужно выполнять все проверки для каждого номера версии в каждом методе действия. Каждый контроллер применим только к одной версии API.

Для меня это чище (IMO) обрабатывать версии во время маршрута, а не во время действия. Вы можете сделать это с ограничением маршрутизации, чтобы проверить номер версии.

В приведенном ниже примере контроллеры V10 и V20 могут быть перенаправлены только в том случае, если ограничение маршрута прошло, т.е. заголовок присутствовал, если нет заголовка по умолчанию (то есть 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) }
        );

Вы можете сделать это, используя строку запроса, чтобы передать версию, как вы это делаете в настоящее время, и просто перейти на ограничение маршрута, однако мне было проще поддерживать использование необязательного заголовка в запросе. (это также более «RESTful», если не вдаваться во все эти дебаты). Отсутствие заголовка означает стандартную (последнюю) версию API.

Пример ограничения версии API:

/// <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
}
person Rosstified    schedule 23.02.2010
comment
на самом деле это довольно крутая идея :) мне она очень нравится. Но со значениями заголовка я не могу просто вызвать API из браузера, правда? - person Pure.Krome; 23.02.2010

Просто чтобы добавить что-то еще к этому старому вопросу... Вы можете комбинировать методы здесь и, таким образом, поддерживать выбор версии по заголовку запроса ИЛИ строке запроса, изменив проверку в ответе @Rosstified, чтобы включить проверку 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.
            }
        }
person Elliveny    schedule 23.01.2015