C# 基于web api 2属性的路由生成

C# 基于web api 2属性的路由生成,c#,asp.net-mvc-5,asp.net-web-api-routing,C#,Asp.net Mvc 5,Asp.net Web Api Routing,我一直在搞MVC5和WebApi2。在某一点上,RouteAttributes似乎有一个基于约定的自动名称——“ControllerName.ActionName”。我有一个包含许多APIController的大型api和一个使用属性定义的自定义路由。我可以直接使用URL,它工作得很好,ApiExplorer也可以很好地使用它 然后,我需要为dto对象中的某些字段生成链接和更新URL。我试着打电话: Url.Link(“,新{controller=“…”,action=“…”,[other da

我一直在搞MVC5和WebApi2。在某一点上,RouteAttributes似乎有一个基于约定的自动名称——“ControllerName.ActionName”。我有一个包含许多APIController的大型api和一个使用属性定义的自定义路由。我可以直接使用URL,它工作得很好,ApiExplorer也可以很好地使用它

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

Url.Link(“,新{controller=“…”,action=“…”,[other data…]})

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

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

如果您有任何意见,我们将不胜感激。

您怎么办?也许您可以像在视图中一样在DTO中公开这些内容

控制器

[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }`
<a href="@Url.RouteUrl("mainmenu")">Main menu</a>
查看

[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }`
<a href="@Url.RouteUrl("mainmenu")">Main menu</a>

使用,我选择使用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;
    }
}
[RoutePrefix(“api/v2/test”)]
公共类实体控制器:ApiController{
[路线(“”)
公共IEnumerable GetAll(){
// ...
}
[路由(“{id:int}”)]
公共实体Get(int-id){
// ...
}
//…东西
[HttpGet]
[路由(“{id:int}/children”)]
公共IEnumerable[Child]子对象(int-id){
// ...
}
}
///
///别处
///
//输出:api/v2/test/5
request.HttpRouteUrl(HttpMethod.Get,新建{
controller=“entity”,
id=5
} )
//输出:api/v2/测试/5/儿童
request.HttpRouteUrl(HttpMethod.Get,新建{
controller=“entity”,
action=“儿童”,
id=5
} )
以下是实现方法:

[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;
    }
}
公共静态类HttpRouteUrlExtension{
私有常量字符串HttpRouteKey=“httproute”;
私有静态只读类型[]SimpleType=new[]{
类型(日期时间),
类型(十进制),
类型(Guid),
类型(字符串),
类型(时间跨度)
};
公共静态字符串HttpRouteUrl(此HttpRequestMessage请求、HttpMethod方法、对象RouteValue){
返回HttpRouteUrl(请求、方法、新HttpRouteValueDictionary(routeValues));
}
公共静态字符串HttpRouteUrl(此HttpRequestMessage请求、HttpMethod方法、IDictionary routeValues){
if(routeValues==null){
抛出新ArgumentNullException(“RouteValue”);
}
如果(!routeValues.ContainsKey(“控制器”)){
抛出新ArgumentException(“'controller'键必须提供”,“RouteValue”);
}
routeValues=新的HttpRouteValueDictionary(routeValues);
如果(!routeValues.ContainsKey(HttpRouteKey)){
Add(HttpRouteKey,true);
}
字符串controllerName=routeValues[“controller”].ToString();
RouteValue.移除(“控制器”);
string actionName=string.Empty;
if(routeValues.ContainsKey(“操作”)){
actionName=routeValues[“action”].ToString();
routeValues.移除(“操作”);
}
IHttpRoute[]matchedRoutes=request.GetConfiguration().Services
.GetApiExplorer().ApiDescriptions
.Where(x=>x.ActionDescriptor.ControllerDescriptor.ControllerName.Equals(ControllerName,StringComparison.OrdinalIgnoreCase))
.Where(x=>x.ActionDescriptor.SupportedHttpMethods.Contains(方法))
.Where(x=>string.IsNullOrEmpty(actionName)| | x.ActionDescriptor.actionName.Equals(actionName,StringComparison.OrdinalIgnoreCase))
.选择(x=>new{
路线=x.路线,
matches=x.ActionDescriptor.GetParameters()
.Count(p=>(!p.IsOptional)&&
(p.ParameterType.IsPrimitive | | SimpleType.Contains(p.ParameterType))&&
(routeValues.ContainsKey(p.ParameterName))&&
(RouteValue[p.ParameterName].GetType()==p.ParameterType))
} )
.其中(x=>x.matches>0)
.OrderBy(x=>x.route.DataTokens[“order”])
.ThenBy(x=>x.route.DataTokens[“优先级”])
.然后按降序排列(x=>x.matches)
.选择(x=>x.route)
.ToArray();
如果(matchedRoutes.Length>0){
IHttpVirtualPathData pathData=matchedRoutes[0]。GetVirtualPath(请求,路由值);
if(pathData!=null){
返回新Uri(新Uri(httpRequestMessage.RequestUri.GetLeftPart(UriPartial.Authority)),pathData.VirtualPath);
}
}
返回null;
}
}

那就行了,问题在于自定义api路由。为每一个人指定一个名字是一件非常麻烦的事情。我想知道是否有办法绕过这个问题。嗯,我想到了反思。你看过蒂姆·斯科特的吗?也许是因为你的身体不适,或者让你走上了正轨。我明天上班的时候会看一看。我已经使用ApiExplorer和T4为客户端上的api助手方法生成javascript。我可能会那么可怕