3

我一直在搞乱 MVC5 和 WebApi2。在某一时刻,RouteAttributes 似乎有一个基于约定的自动名称——“ControllerName.ActionName”。我有一个带有许多 ApiControllers 的大型 api 和一个使用属性定义的自定义路由。我可以直接使用这些 url,而且效果很好,ApiExplorer 也可以很好地使用它。

然后,我需要生成链接,并为我的 dto 对象中的某些字段生成更新 url。我试过打电话:

Url.Link("", new { controller = "...", action = "...", [其他数据...] })

但它使用定义的默认全局路由,该路由不可用。

是否无法为没有使用 UrlHelper.Link 定义的名称的基于属性的路由生成链接?

任何输入将不胜感激,谢谢。

4

2 回答 2

1

路线名称呢?也许您可以像在视图中一样在 DTO 中公开它们。

控制器

[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }`

查看

<a href="@Url.RouteUrl("mainmenu")">Main menu</a>
于 2013-10-31T20:30:52.133 回答
1

使用此处描述的算法,我选择使用 ApiExplorer 来获取与给定值集匹配的路由。

使用示例:

[RoutePrefix( "api/v2/test" )]
public class EntityController : ApiController {
    [Route( "" )]
    public IEnumerable<Entity> GetAll() {
        // ...
    }

    [Route( "{id:int}" )]
    public Entity Get( int id ) {
        // ...
    }

    // ... stuff

    [HttpGet]
    [Route( "{id:int}/children" )]
    public IEnumerable[Child] Children( int id ) {
        // ...
    }
}

///
/// elsewhere
///

// outputs: api/v2/test/5
request.HttpRouteUrl( HttpMethod.Get, new {
    controller = "entity",
    id = 5
} )

// outputs: api/v2/test/5/children
request.HttpRouteUrl( HttpMethod.Get, new {
    controller = "entity",
    action = "children",
    id = 5
} )

这是实现:

public static class HttpRouteUrlExtension {
    private const string HttpRouteKey = "httproute";

    private static readonly Type[] SimpleTypes = new[] {
        typeof (DateTime), 
        typeof (Decimal), 
        typeof (Guid), 
        typeof (string), 
        typeof (TimeSpan)
    };

    public static string HttpRouteUrl( this HttpRequestMessage request, HttpMethod method, object routeValues ) {
        return HttpRouteUrl( request, method, new HttpRouteValueDictionary( routeValues ) );
    }

    public static string HttpRouteUrl( this HttpRequestMessage request, HttpMethod method, IDictionary<string, object> routeValues ) {
        if ( routeValues == null ) {
            throw new ArgumentNullException( "routeValues" );
        }

        if ( !routeValues.ContainsKey( "controller" ) ) {
            throw new ArgumentException( "'controller' key must be provided", "routeValues" );
        }

        routeValues = new HttpRouteValueDictionary( routeValues );
        if ( !routeValues.ContainsKey( HttpRouteKey ) ) {
            routeValues.Add( HttpRouteKey, true );
        }

        string controllerName = routeValues[ "controller" ].ToString();
        routeValues.Remove( "controller" );

        string actionName = string.Empty;
        if ( routeValues.ContainsKey( "action" ) ) {
            actionName = routeValues[ "action" ].ToString();
            routeValues.Remove( "action" );
        }

        IHttpRoute[] matchedRoutes = request.GetConfiguration().Services
                                            .GetApiExplorer().ApiDescriptions
                                            .Where( x => x.ActionDescriptor.ControllerDescriptor.ControllerName.Equals( controllerName, StringComparison.OrdinalIgnoreCase ) )
                                            .Where( x => x.ActionDescriptor.SupportedHttpMethods.Contains( method ) )
                                            .Where( x => string.IsNullOrEmpty( actionName ) || x.ActionDescriptor.ActionName.Equals( actionName, StringComparison.OrdinalIgnoreCase ) )
                                            .Select( x => new {
                                                route = x.Route,
                                                matches = x.ActionDescriptor.GetParameters()
                                                           .Count( p => ( !p.IsOptional ) &&
                                                                   ( p.ParameterType.IsPrimitive || SimpleTypes.Contains( p.ParameterType ) ) &&
                                                                   ( routeValues.ContainsKey( p.ParameterName ) ) &&
                                                                   ( routeValues[ p.ParameterName ].GetType() == p.ParameterType ) )
                                            } )
                                            .Where(x => x.matches > 0)
                                            .OrderBy( x => x.route.DataTokens[ "order" ] )
                                            .ThenBy( x => x.route.DataTokens[ "precedence" ] )
                                            .ThenByDescending( x => x.matches )
                                            .Select( x => x.route )
                                            .ToArray();

        if ( matchedRoutes.Length > 0 ) {
            IHttpVirtualPathData pathData = matchedRoutes[ 0 ].GetVirtualPath( request, routeValues );

            if ( pathData != null ) {
                return new Uri( new Uri( httpRequestMessage.RequestUri.GetLeftPart( UriPartial.Authority ) ), pathData.VirtualPath ).AbsoluteUri;
            }
        }

        return null;
    }
}
于 2013-11-04T18:50:11.043 回答