WebApi2

时间:2015-08-31 17:37:01

标签: asp.net-web-api2 asp.net-web-api asp.net-web-api-routing

我正在使用WebApi2 attrbiute路由项目,我试图对路由进行单元测试(请求在控制器中执行正确的api方法)。但到目前为止我还没能让它发挥作用......

似乎没有选择属性中定义的路线 知道什么可能是错的或丢失了吗?

提前致谢!吉列尔莫。

这是我的单元测试代码:

var config = new HttpConfiguration
{
    IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always,
};

config.Routes.MapHttpRoute(name: "Default", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });

request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/homes/report");
route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
routeData = new HttpRouteData(route, new HttpRouteValueDictionary
    {
        { "controller", "homes" },
    });

config.MapHttpAttributeRoutes();
config.EnsureInitialized();

controller = new HomesController
{
    Catalog = catalog.Object,
    ControllerContext = new HttpControllerContext(config, routeData, request),
    Request = request,
};

controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;

var routeTester = new RouteTester(config, request, controller.ControllerContext);

Assert.IsTrue(routeTester.CompareSignatures(ReflectionHelpers.GetMethodInfo((HomesController p) => p.GetHomesReport())));

RouteTester.cs

public class RouteTester
{
    readonly HttpConfiguration config;
    readonly HttpRequestMessage request;
    readonly IHttpRouteData routeData;
    readonly IHttpControllerSelector controllerSelector;
    readonly HttpControllerContext controllerContext;

    public RouteTester(HttpConfiguration conf, HttpRequestMessage req, HttpControllerContext context)
    {
        config = conf;
        request = req;
        controllerContext = context;
        routeData = context.RouteData;

        controllerSelector = new DefaultHttpControllerSelector(config);
    }

    public string GetActionName()
    {
        if (controllerContext.ControllerDescriptor == null)
            GetControllerType();

        var actionSelector = new ApiControllerActionSelector();
        var descriptor = actionSelector.SelectAction(controllerContext);

        return descriptor.ActionName;
    }

    public Type GetControllerType()
    {
        var descriptor = controllerSelector.SelectController(request);
        controllerContext.ControllerDescriptor = descriptor;
        return descriptor.ControllerType;
    }

    public bool CompareSignatures(MethodInfo method)
    {
        if (controllerContext.ControllerDescriptor == null)
            GetControllerType();


        var actionSelector = new ApiControllerActionSelector();
        var x = actionSelector.GetActionMapping(controllerContext.ControllerDescriptor)[request.Method.ToString()];


        return x.Any(item => ((MethodBase)(((ReflectedHttpActionDescriptor)item).MethodInfo)).ToString() == ((MethodBase)method).ToString());
    }

ReflectionHelpers.cs

 public class ReflectionHelpers
{
    public static string GetMethodName<T, U>(Expression<Func<T, U>> expression)
    {
        var method = expression.Body as MethodCallExpression;

        if (method != null)
            return method.Method.Name;

        throw new ArgumentException("Expression is wrong");
    }

    public static MethodInfo GetMethodInfo<T, U>(Expression<Func<T, U>> expression)
    {
        var method = expression.Body as MethodCallExpression;
        if (method != null)
            return method.Method;


        throw new ArgumentException("Expression is wrong");
    }


    public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var method = expression.Body as MethodCallExpression;
        if (method != null)
            return method.Method;


        throw new ArgumentException("Expression is wrong");
    }
}

控制器代码段

[Route("api/homes/homereport")]
public void GetHomesReport()
{
    var homeReportItems = HomeReport();
}

1 个答案:

答案 0 :(得分:0)

不确定它是否是您问题的解决方案,但我使用此方法来控制属性路由。 它会强制您指定控制器类和方法签名,但所有都没有字符串文字,因此您可以在此处获得良好的编译时间验证。 FunctionSignature用于公共字符串Action(int value)的简单方法,它看起来像Func<int, string>

测试方法如下所示:

public void TestMethod() {
    var info = GetEndpointInfo<ControllerClass, FunctionSignature>(c => c.Action);

    Assert.IsNotNull(info.AllowsAnonymous);
    Assert.AreEqual("data/places/{query}", info.Route);
}

这里有一些lambda和反射暴力来获取元信息:

private EndpointInfo GetEndpointInfo<TController, TMethod>(Expression<Func<TController, TMethod>> expression) {
    var controllerType = typeof(TController);
    var prefix = controllerType.GetCustomAttribute<RoutePrefixAttribute>().Prefix;
    MethodInfo methodInfo = GetMemberInfo(expression);
    var template = methodInfo.GetCustomAttribute<RouteAttribute>().Template;

    var info = new EndpointInfo() {
        AllowsAnonymous = controllerType.GetCustomAttribute<AllowAnonymousAttribute>() != null, //etend here to check method level attr
        Route = prefix + "/" + template
    };
    return info;
}

private MethodInfo GetMemberInfo<TController, TMethod>(Expression<Func<TController, TMethod>> expression) {
    var unaryExpression = expression.Body as UnaryExpression;
    var methodCall = unaryExpression.Operand as MethodCallExpression;
    var constant = methodCall.Object as ConstantExpression;
    var methodInfo = constant.Value as MethodInfo;
    return methodInfo;
}
class EndpointInfo{
    public bool AllowsAnonymous { get; set; }
    public string Route { get; set; }
}

希望这有帮助。