我目前正在开发一组MVC3 Web应用程序,这些应用程序有许多共同点,但是作为单独的程序包部署。一个例外是为每个安装部署的管理应用程序。他们还共享一个数据库和一组共同实体。
最近增加的内容包括身份验证(自定义表单身份验证),自动菜单构建(使用控制器操作属性)和其他一些屏幕。我遇到的问题是我必须创建重复的控制器和视图。
最好的例子是身份验证位。每个应用程序现在都有一个帐户控制器和身份验证视图,它们是彼此的副本。您可以看到,由于添加了更多应用程序,这很快就会成为维护噩梦。另一个例子是每个呈现菜单的应用程序中的共享视图(通常通过Html.Action调用)。这还需要一个在每个应用程序中看起来完全相同的控制器。
我设法将身份验证和菜单构建的实现细节抽象为一个名为XXX.Core.Mvc的共享项目,该项目还具有常见的htmlHelper扩展和Mvc web特有的任何内容。所以现在应用程序中所有重复的视图和控制器都充当代理。
我正在寻找一个关于如何摆脱这些的好模式,因此常见的视图和控制器将驻留在可以引用/调用它们的共享项目中。有没有人做过这样的事情?你能推荐一些好的文章或例子吗?
例如,如何指定将View ActionResult返回到不同项目或共享路径中的视图的控制器操作?如何将例如/ Account / Authenticate路由到另一个项目中的控制器?方法的部署含义是什么?
答案 0 :(得分:0)
我尝试了两件事。在每种情况下,公共控制器和视图都位于单独的组件中。为了概念验证,我实现了常见的应用程序错误处理所以在我的终点Mvc3应用程序中,global.asax实现:
protected void Application_Error()
{
ApplicationErrorHandler.Get().Handle(Server, new HttpResponseWrapper(Response), new HttpContextWrapper(Context));
}
在单独的程序集中实现Application错误处理程序:
public class ApplicationErrorHandler
{
public static ApplicationErrorHandler Get()
{
return new ApplicationErrorHandler();
}
public void Handle(HttpServerUtility server, HttpResponseBase response, HttpContextBase context)
{
var exception = server.GetLastError();
var httpException = exception as HttpException;
response.Clear();
server.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "Errors";
routeData.Values["action"] = "Http500";
routeData.Values["exception"] = exception;
response.StatusCode = 500;
if (httpException != null)
{
response.StatusCode = httpException.GetHttpCode();
switch (response.StatusCode)
{
case 403:
routeData.Values["action"] = "Http403";
break;
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
IController errorsController = new ErrorsController();
var rc = new RequestContext(context, routeData);
errorsController.Execute(rc);
}
}
在Controllers文件夹中的同一个程序集中,ErrorsController和Views \ Errors保存了所需的3个视图; Http403.cshtml,Http404.cshtml和Http405.cshtml。所以结构与普通的Mvc3项目相同。但是,为了使@model支持和intelisense工作,您还需要web.config - 来自常规项目中Views的文件的副本,并放置在此单独程序集中的视图下。
当我们的应用程序中发生应用程序错误(http状态500 - 内部服务器错误)时,执行ErrorsController,在这种情况下使用的视图将是Http500.cshtml。调查的路径将是:
为了从程序集中提供文件,我尝试了两件事:
方法一:实现VirtualPathProvider。我不会详细介绍,但目标是将cshtml视图作为资源嵌入,并使用VirtualPathProvider和VirtualFile为它们提供服务。这种方法的问题是我发现没有可行的方法来从Views文件夹中为web.config提供服务,以便让Razor评估视图。我可以让它检索文件并流式传输但不执行它。要解释问题所在,请考虑Http500.cshtml:
@model Exception
<h3>@MvcHtmlString.Create(Model.Message.Replace(Environment.NewLine, "<br />").Replace("\t", " "))</h3>
<p>@MvcHtmlString.Create(Model.StackTrace.Replace(Environment.NewLine, "<br />").Replace("\t", " "))</p>
<input type="hidden" name="serverError" value="true"/>
返回的是文件的内容而不是评估的Exception。原因是需要提供View文件夹中的web.config以便使用Razor。默认情况下,视图引擎是Web表单(.aspx)。
方法二:在IIS中创建虚拟目录。这种方法效果更好,因为视图不必编译为资源,不需要虚拟化。所需的只是在我的最终应用程序中的Views文件夹下创建虚拟目录错误,并将其指向部署Views / Errors from common assembly的位置。
当然,它不像一个正确的插件,因为常见的文件需要与.dll分开部署,但我并不是太烦恼。
然而,如果有人知道如何让虚拟文件为Razor工作,我会全力以赴。嗯 - 所有人的眼睛真的。