我正在通过一些ASP.NET MVC阅读我的路,我在工作中有一个web应用程序,我会从迁移到的WebForms MVC。 一个我希望在这个过程中获得的功能要求是,如果用户从移动设备来已经返回简化视图。
我不能完全看到的最好的地方是实现类型的逻辑。 我敢肯定有比在每一个返回一个视图动作添加的if / else对于Browser.IsMobileDevice一个更好的办法。 我将有什么样的选择,这样做呢?
我正在通过一些ASP.NET MVC阅读我的路,我在工作中有一个web应用程序,我会从迁移到的WebForms MVC。 一个我希望在这个过程中获得的功能要求是,如果用户从移动设备来已经返回简化视图。
我不能完全看到的最好的地方是实现类型的逻辑。 我敢肯定有比在每一个返回一个视图动作添加的if / else对于Browser.IsMobileDevice一个更好的办法。 我将有什么样的选择,这样做呢?
更新 :该解决方案有一个微妙的错误。 MVC框架将调用FindView
/ FindPartialView
用一次:两次useCache=true
,如果不与返回结果,一旦useCache=false
。 由于只有一个对所有类型的视图缓存,移动用户可能最终看到桌面视图如果桌面浏览器是第一个到达的。
对于那些热衷于使用自定义视图引擎来解决这个问题,Scott Hanselman在这里更新了他的解决方案:
http://www.hanselman.com/blog/ABetterASPNETMVCMobileDeviceCapabilitiesViewEngine.aspx
(道歉答案劫持,我只是不希望其他人必须要经过这个!)
通过roufamatic编辑(2010-11-17)
你要做的第一件事是推出移动设备浏览器的文件到您的项目。 使用这个文件你可以针对你希望在不必知道什么这些设备在他们的头发送的具体支持什么都装置。 这个文件已经为你做的工作。 然后,您使用的Request.Browser特性量身定制一个视图要返回。
接下来,拿出你要如何组织浏览文件夹下你的观点的战略。 我宁愿让在根的桌面版本,然后有一个移动的文件夹。 例如在主页视图文件夹是这样的:
我有@Mehrdad不同意有关使用自定义视图引擎。 视图引擎提供多于一个的目的和那些目的之一是找到视图用于控制器更多。 您可以通过重写FindView方法做到这一点。 在这种方法中,你可以做你在哪里可以找到该视图检查。 你知道哪些设备正在使用你的网站后,你可以使用你想出了组织你的意见返回该设备的视图的策略。
public class CustomViewEngine : WebFormViewEngine
{
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
// Logic for finding views in your project using your strategy for organizing your views under the Views folder.
ViewEngineResult result = null;
var request = controllerContext.HttpContext.Request;
// iPhone Detection
if (request.UserAgent.IndexOf("iPhone",
StringComparison.OrdinalIgnoreCase) > 0)
{
result = base.FindView(controllerContext, "Mobile/iPhone/" + viewName, masterName, useCache);
}
// Blackberry Detection
if (request.UserAgent.IndexOf("BlackBerry",
StringComparison.OrdinalIgnoreCase) > 0)
{
result = base.FindView(controllerContext, "Mobile/BlackBerry/" + viewName, masterName, useCache);
}
// Default Mobile
if (request.Browser.IsMobileDevice)
{
result = base.FindView(controllerContext, "Mobile/" + viewName, masterName, useCache);
}
// Desktop
if (result == null || result.View == null)
{
result = base.FindView(controllerContext, viewName, masterName, useCache);
}
return result;
}
}
上面的代码,您可以设置基于策略的看法。 回退是桌面视图,如果没有视图被发现的设备或如果没有默认移动视图。
如果你决定把逻辑控制器的,而不是创建一个视图引擎。 最好的方法是创建一个自定义ActionFilterAttribute ,您可以用装饰你的控制器。 然后重写OnActionExecuted方法,以确定哪些设备正在浏览您的网站。 您可以检查此博客帖子就如何。 该帖子还对这个主题了一些不错的链接到一些混合视频。
在Model-View-Controller模式,那就是选择视图控制器,因此,它不是坏的增加if
语句并返回相应的视图。 您可以封装if
声明的方法,并调用它:
return AdaptedView(Browser.IsMobileDevice, "MyView.aspx", model);
或者,你可以创建动态执行基于它是否移动还是不是一个视图视图引擎。 我不是这种方法的粉丝,因为我相信,控制器应负责。 举例来说,如果你在iPhone上浏览,你可能希望看到完整的桌面版本代替。 在前一种方法中,你会通过适当的布尔标志,但在后者,事情变得更加复杂。
我认为,以堵塞该功能在正确的地方是自定义视图引擎。 但是,你应该知道如何IViewEngine.FindView
方法是由被叫ViewEngineCollection
(找到更多关于这个在这里 )。
更新的解决方案由Scott Hanselman的建议无法正常工作。 你可以找到我的这种方法的样本实现在这里 。 检查介绍如何重复不正确的行为自述文件。
我建议另一种方法,如果一个观点没有被原来的视图引擎发现,如果检查useCache
参数是true
,它如果鉴于原有的视图引擎与参数存在检查useCache=false
。
它太复杂,在这里把所有的代码,但你可以找到自己的开源操场实施的建议的方法在这里 。 检查MobileViewEngine
类和单元测试。
一些MobileViewEngine特点:
Mobile/Platform/Index
-若存在视图和移动设备平台(IPhone,机器人等)在支持列表征。 Mobile/Index
-视图所有其他移动设备。 如果视图不存在,你可以选择退回到桌面视图版本。 Index
-桌面视图版本。 Mobile/ Platform/Manufacturer
)或添加/更改设备的规则(见定制移动视图路径解析MobileDeviceRule
和PlatformSpecificRule
)。 希望,这将有助于
这是实际工作,都与T4MVC和释放模式(在启用视图缓存)版本。 它照顾用户控件和绝对/相对URL的为好。 它要求移动设备浏览器的文件 。
public class MobileCapableWebFormViewEngine : WebFormViewEngine
{
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
if (viewPath.EndsWith(".ascx"))
masterPath = "";
return base.CreateView(controllerContext, viewPath, masterPath);
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
useCache = false;
ViewEngineResult result = null;
var request = controllerContext.HttpContext.Request;
if (request.Browser.IsMobileDevice || request["mobile"] != null || request.Url.Host.StartsWith("m."))
{
var mobileViewName = GetMobileViewName(viewName);
result = base.FindView(controllerContext, mobileViewName, masterName, useCache);
if (result == null || result.View == null)
{
result = base.FindView(controllerContext, viewName, "Mobile", useCache);
}
}
if (result == null || result.View == null)
{
result = base.FindView(controllerContext, viewName, masterName, useCache);
}
return result;
}
private static string GetMobileViewName(string partialViewName)
{
var i = partialViewName.LastIndexOf('/');
return i > 0
? partialViewName.Remove(i) + "/Mobile" + partialViewName.Substring(i)
: "Mobile/" + partialViewName;
}
}
你的核心逻辑应该是相同的控制器,只有你需要将改变视图,控制器是在你需要的if / else语句就可以提供正确的观点为每一个控制器行动,你说。
另一种方法是缠绕你控制器逻辑在一个单独的DLL,并然后具有不同的控制器/路径的移动版本。 如果一个普通控制器从移动设备接收到一个请求,你可以将其重定向到包含使用共享控制器逻辑所有的移动控制器移动区域。 该解决方案还允许你做“tweeks”特定于移动控制器并且不让它影响你的正常控制器。