문제

충돌하는 두 가지 행동 방법이 있습니다. 기본적으로 항목의 ID 또는 항목 이름과 부모의 이름과 부모의 이름을 사용하여 두 개의 다른 경로를 사용하여 동일한보기를 원합니다 (항목은 다른 부모의 이름을 가질 수 있음). 검색어를 사용하여 목록을 필터링 할 수 있습니다.

예를 들어...

Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321

내 행동 방법은 다음과 같습니다 Remove 액션 방법) ...

// Method #1
public ActionResult Assign(string parentName, string itemName) { 
    // Logic to retrieve item's ID here...
    string itemId = ...;
    return RedirectToAction("Assign", "Items", new { itemId });
}

// Method #2
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... }

그리고 여기 경로가 있습니다 ...

routes.MapRoute("AssignRemove",
                "Items/{action}/{itemId}",
                new { controller = "Items" }
                );

routes.MapRoute("AssignRemovePretty",
                "Items/{action}/{parentName}/{itemName}",
                new { controller = "Items" }
                );

오류가 발생하는 이유를 이해합니다. page 매개 변수는 null 일 수 있지만 그것을 해결하는 가장 좋은 방법을 알 수는 없습니다. 내 디자인이 처음부터 가난합니까? 나는 확장에 대해 생각했다 Method #1검색 매개 변수를 포함하고 로직 이동하는 서명 Method #2 개인 방법으로 그들은 둘 다 부를 것입니다. 그러나 나는 그것이 실제로 모호성을 해결할 것이라고 믿지 않습니다.

모든 도움은 대단히 감사하겠습니다.


실제 솔루션 (Levi의 답변에 근거)

다음 수업을 추가했습니다 ...

public class RequireRouteValuesAttribute : ActionMethodSelectorAttribute {
    public RequireRouteValuesAttribute(string[] valueNames) {
        ValueNames = valueNames;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
        bool contains = false;
        foreach (var value in ValueNames) {
            contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value);
            if (!contains) break;
        }
        return contains;
    }

    public string[] ValueNames { get; private set; }
}

그리고 액션 방법을 장식했습니다 ...

[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }

[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
도움이 되었습니까?

해결책

MVC는 서명 만 기반으로 메소드 과부하를 지원하지 않으므로 실패합니다.

public ActionResult MyMethod(int someInt) { /* ... */ }
public ActionResult MyMethod(string someString) { /* ... */ }

그러나, 그것은 하다 지지 메소드 과부하 속성을 기반으로합니다.

[RequireRequestValue("someInt")]
public ActionResult MyMethod(int someInt) { /* ... */ }

[RequireRequestValue("someString")]
public ActionResult MyMethod(string someString) { /* ... */ }

public class RequireRequestValueAttribute : ActionMethodSelectorAttribute {
    public RequireRequestValueAttribute(string valueName) {
        ValueName = valueName;
    }
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
        return (controllerContext.HttpContext.Request[ValueName] != null);
    }
    public string ValueName { get; private set; }
}

위의 예에서 속성은 단순히 "이 메소드가 키가 일치합니다. 트리플 엑스 "요청에 참석 한 경우 (ControlLerConText.RequestContext) Route에 포함 된 정보로 필터링 할 수도 있습니다.

다른 팁

경로의 매개 변수 {roleId}, {applicationName} 그리고 {roleName} 동작 방법의 매개 변수 이름과 일치하지 마십시오. 그것이 중요한지 모르겠지만, 당신의 의도가 무엇인지 알아내는 것이 더 어렵습니다.

ItemId는 Regex를 통해 일치 할 수있는 패턴을 준수합니까? 그렇다면 패턴과 일치하는 URL만이 ItemID를 포함하는 것으로 식별되도록 경로에 구속을 추가 할 수 있습니다.

ItemID가 숫자 만 포함 된 경우 다음에 효과가 있습니다.

routes.MapRoute("AssignRemove",
                "Items/{action}/{itemId}",
                new { controller = "Items" },
                new { itemId = "\d+" }
                );

편집 : 당신은 또한 AssignRemovePretty 둘 다 {parentName} 그리고 {itemName} 필요합니다.

편집 2 : 또한 첫 번째 액션이 두 번째 액션으로 리디렉션되므로 첫 번째 액션을 이름 바꾸면 모호함을 제거 할 수 있습니다.

// Method #1
public ActionResult AssignRemovePretty(string parentName, string itemName) { 
    // Logic to retrieve item's ID here...
    string itemId = ...;
    return RedirectToAction("Assign", itemId);
}

// Method #2
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... }

그런 다음 경로에서 조치 이름을 지정하여 적절한 방법을 강제로 호출하도록합니다.

routes.MapRoute("AssignRemove",
                "Items/Assign/{itemId}",
                new { controller = "Items", action = "Assign" },
                new { itemId = "\d+" }
                );

routes.MapRoute("AssignRemovePretty",
                "Items/Assign/{parentName}/{itemName}",
                new { controller = "Items", action = "AssignRemovePretty" },
                new { parentName = "\w+", itemName = "\w+" }
                );

또 다른 접근법은 방법 중 하나의 이름을 바꾸므로 충돌이 없습니다. 예를 들어

// GET: /Movies/Delete/5
public ActionResult Delete(int id = 0)

// POST: /Movies/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id = 0)

보다 http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs

최근에 나는 다중 매개 변수 지원, 그들과 일치시키는 것과 일치시키고 심지어 그들 중 어느 것도 일치시키기 위해 @Levi의 답변을 개선하기 위해 @Levi의 답변을 개선 할 기회를 얻었습니다.

내가 지금 사용하고있는 속성은 다음과 같습니다.

/// <summary>
/// Flags an Action Method valid for any incoming request only if all, any or none of the given HTTP parameter(s) are set,
/// enabling the use of multiple Action Methods with the same name (and different signatures) within the same MVC Controller.
/// </summary>
public class RequireParameterAttribute : ActionMethodSelectorAttribute
{
    public RequireParameterAttribute(string parameterName) : this(new[] { parameterName })
    {
    }

    public RequireParameterAttribute(params string[] parameterNames)
    {
        IncludeGET = true;
        IncludePOST = true;
        IncludeCookies = false;
        Mode = MatchMode.All;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        switch (Mode)
        {
            case MatchMode.All:
            default:
                return (
                    (IncludeGET && ParameterNames.All(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    || (IncludePOST && ParameterNames.All(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    || (IncludeCookies && ParameterNames.All(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
            case MatchMode.Any:
                return (
                    (IncludeGET && ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    || (IncludePOST && ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    || (IncludeCookies && ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
            case MatchMode.None:
                return (
                    (!IncludeGET || !ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    && (!IncludePOST || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    && (!IncludeCookies || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
        }
    }

    public string[] ParameterNames { get; private set; }

    /// <summary>
    /// Set it to TRUE to include GET (QueryStirng) parameters, FALSE to exclude them:
    /// default is TRUE.
    /// </summary>
    public bool IncludeGET { get; set; }

    /// <summary>
    /// Set it to TRUE to include POST (Form) parameters, FALSE to exclude them:
    /// default is TRUE.
    /// </summary>
    public bool IncludePOST { get; set; }

    /// <summary>
    /// Set it to TRUE to include parameters from Cookies, FALSE to exclude them:
    /// default is FALSE.
    /// </summary>
    public bool IncludeCookies { get; set; }

    /// <summary>
    /// Use MatchMode.All to invalidate the method unless all the given parameters are set (default).
    /// Use MatchMode.Any to invalidate the method unless any of the given parameters is set.
    /// Use MatchMode.None to invalidate the method unless none of the given parameters is set.
    /// </summary>
    public MatchMode Mode { get; set; }

    public enum MatchMode : int
    {
        All,
        Any,
        None
    }
}

추가 정보 및 방법 구현 샘플을 보려면 확인하십시오 이 블로그 게시물 이 주제에 대해 썼습니다.

routes.MapRoute("AssignRemove",
                "Items/{parentName}/{itemName}",
                new { controller = "Items", action = "Assign" }
                );

MVC 사용을 고려하여 경로를 테스트하기 위해 테스트 경로 라이브러리를 고려하십시오.

"Items/parentName/itemName".Route().ShouldMapTo<Items>(x => x.Assign("parentName", itemName));
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top