代码改变世界

web api中的RouteHandler

2016-02-24 12:05  滴答滴_zsj  阅读(1256)  评论(0编辑  收藏  举报

ASP.NET MVC4中引入的Web API可以说是进行REST软件开发的利器(个人意见),但是最近在web form中混入web api时,发现一个问题:由于以前的web form项目中,使用到了session(包括那些复杂的底层逻辑),所以为了最小改动,必须保证web api能支持session。而web api默认情况下,是不支持session的。

问题重现

重现这个不支持session的问题,其实很简单。只需要新建一个web api项目,然后修改ValuesController的Get方法为:

public string Get()
{
    return HttpContext.Current.Session == null ? "Session is null" : string.Format("SessionID is '{0}'", HttpContext.Current.Session.SessionID);
}

在运行后,发现页面输出为:"Session is null"。

解决方案

经过一番努力,发现Route中有一个IRouteHandler类型的属性RouteHandler,而默认情况下,这个RouteHandler是System.Web.Http.WebHost.HttpControllerRouteHandler,而HttpControllerRouteHandler中的GetHttpHandler会返回一个IHttpHandler的对象,来对web api的请求进行预处理。对于IHttpHandler其实大家应该不陌生,这个正是asp.net web form中,经常使用到的iis管道处理的一个环节。那么为了让IHttpHandler支持session,我们知道只需要实现一个标记接口IRequiresSessionState。而默认的System.Web.Http.WebHost.HttpControllerHandler只实现了IHttpHandler并没有实现IRequiresSessionState。所以问题就变成了如何让IRouteHandler的GetHttpHandler方法返回一个既实现了IHttpHandler,又实现了IRequiresSessionState对象。所以有了下面的类:

public class WebApiSessionControllerHandler : HttpControllerHandler, IRequiresSessionState 
{
    public WebApiSessionControllerHandler(RouteData routeData) : base(routeData) { }        
}

为了使用WebApiSessionControllerHandler,还需要实现一个IRouteHandler自定义类:

public class WebApiSessionRouteHandler : HttpControllerRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new WebApiSessionControllerHandler(requestContext.RouteData);
    }        
}

接下来,就是要让WebApiSessionRouteHandler真正起作用的时候了,只需要在你RegisterRoutes时,把MapHttpRoute修改为:

var route = routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }                    
);
route.RouteHandler = new WebApiSessionRouteHandler(); 

现在再来看下/api/values的输出:"SessionID is 'me4exrjgv4lecvixa0ab2z1g'",发现session不在为null。

优化方案

为了在map route操作时,能够有一个统一的写法,可以增加一个扩展方法:

public static class HttpRouteExtensions
{
    public static Route MapHttpRoute(this RouteCollection routes, string name, string routeTemplate, object defaults, IRouteHandler routeHandler)
    {
        object constraints = null;
        HttpMessageHandler handler = null;
        var route = routes.MapHttpRoute(name, routeTemplate, defaults, constraints, handler);
        if (routeHandler != null)
        {
            route.RouteHandler = routeHandler;
        }
        return route;
    }
}

这样,MapHttpRoute就可以改为:

routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        routeHandler: new WebApiSessionRouteHandler()
);

结论

对HttpControllerHandler使用,不仅仅局限于是web api支持session。前面已经提到我们可以对web api的请求进行预处理,也可以创建自定义类,重现HttpControllerHandler中的:

1、BeginProcessRequest

2、EndProcessRequest

3、ProcessRequest

具体应该重写哪一个就需要根据具体的业务具体分析了。