@wang9563
2017-05-26T20:22:16.000000Z
字数 3273
阅读 1879
问题思考
我们目前的 SongshuERP 项目采用 WebForm 作为主要开发框架,但在实际使用中我们更倾向于 MVC 的开发方式,并在 SongshuERP.Web.UI.dll 的 ManagePage 基类中定义的处理请求的方式。
现在我们尝试在原先用 WebForm 写的代码无需修改为前提,后续新模块直接使用 MVC 进行开发,以下则是介绍了最初版新的开发模式,望大家给予建议不断完善。
目前对原有项目进行了如下更改:
- 新增了项目
SongshuERP.Mvc
用存放 MVC的业务代码,并在SongshuERP.Web
中引用- 在
SongshuERP.Web
的Global.asax
中增加了对SongshuERP.Mvc
的初始化,不需要引用任何 MVC 组件的库
Areas
存放各模块的业务代码,比如
仓储管理
财务管理
等,此处定义了两个文件夹 Admin 和 Demo 表示业务功能不同的两个模块,各自模块下均有 Controller 和 Register
*Controller.cs (例如: HelpController
RuleController
TestController
)
实现具体的业务功能,比如 CURD 、逻辑计算等
Register.cs
注册当前模块的路由,不同的模块有不同的实现,建议默认按 {module}/{controller}/{action} 进行路由配置,如注册一条路由 {admin}/{controller}/{action} 后,通过 URL
http://ip:port/admin/help/index
可访问 Admin 模块下 HelpController 的 Index 方法
PS.
也可以根据自己的业务需要,注册当前模块下的多条路由
Core
存放没有具体业务逻辑的基础组件、帮助类等,如本项目中所有 Controller 的基类
BaseController
ValidateAttribute
存放验证 Model 的 Attribute 类型
Dto
存放用户数据传输的 Model,可以根据需要的 Request 定义 Model 类,如
TestLoginInput
假设需要为一个叫 仓储(Warehouse)的模块增加部分功能,可以按照如下流程编写代码
- 在 Areas 下建立采购模块 Purchase 文件夹,按照功能编写 Controller 如
采购订单(OrderController)
预入库(InStockController)
采购分析(AnalysisController)
等代码
- 添加 Register.cs 并通过以下代码注册本模块的默认路由
using System.Web.Mvc;
namespace SongshuERP.Mvc.Areas.Purchase
{
public class Register : AreaRegistration
{
public override string AreaName => "Purchase";
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"purchase_default",
"purchase/{controller}/{action}");
}
}
}
- 关于实现功能代码可参考 Demo 下的 TestController 实现
不区分大小写
1、直接通过上下文的 Request 对象取值
public ActionResult Login()
{
var email = Request["email"];
var pwd = Request["pwd"];
var code = Request["pwd"];
if (email == "xunkiz@dingtalk.com")
return Json(new { error = "您的账号被禁用了~" });
return Json(new { nick = "鼠西奥" });
}
2、通过参数传入
public ActionResult Login(string email, string pwd, string code)
{
if (email == "xunkiz@dingtalk.com")
return Json(new { error = "您的账号被禁用了~" });
return Json(new { nick = "鼠西奥" });
}
3、通过参数传入,Model 实体对象 字段必须定义成属性
public ActionResult Login(TestLoginInput input)
{
if (input.Email == "xunkiz@dingtalk.com")
return Json(new { error = "您的账号被禁用了~" });
return Json(new { nick = "鼠西奥" });
}
public class TestLoginInput
{
[Required(ErrorMessage = "邮箱不能为空")]
[Mail(ErrorMessage = "邮箱不合法")]
public string Email { get; set; }
[StringLength(6, ErrorMessage = "密码不能超过6位")]
public string Pwd { get; set; }
public string Code { get; set; }
}
在第三种方法中,支持用定义好的 Attribute 来完成验证,其他两种均需要手工实现。第三种方法的实现在 BaseController 中的 OnActionExecuting(请求处理前) 方法中进行了验证,如下代码:
//验证 Model 是否通过
foreach (var m in ModelState.Values)
{
var error = m.Errors.FirstOrDefault();
if (error == null) continue;
filterContext.Result = Json(new SsOutput { ErrMsg = error.ErrorMessage });
return;
}
因为目前都是前端通过 ajax 请求后端的服务,所以定义了 SsOutPut 和 SsOutPut,并声明了他们的隐式转换,如下代码:
public SsOutput<Model> Login3(TestLoginInput input)
{
if (input.Email == "xunkiz@dingtalk.com")
return "您的账号被禁用了~";
return new Model { sign_date = DateTime.Now };
}
如果需要报错,直接返回 string 就会被识别成 ErrMsg 并返回到前端
如果是正常返回内容,直接返回 Model 实例即可,会被识别成 Data 并返回到前端
PS.
当你的 Model 就是 string 类型时,隐式转换会冲突,此时只能手写返回内容
public SsOutput<string> Login(TestLoginInput input)
{
if (input.Email == "test")
return new SsOutput<string> { ErrMsg = "您的账号被禁用了~" };
return new SsOutput<string> { Data = "登录成功!" };
}
通过 Controller 的 Json 方法返回值,以及通过返回 SsOutput 类型的值的 Content-Type 均是 application/json,所以可在前端 ajax 的 success 中直接当对象来使用
$("#login").click(function () {
$.post({
url: "/demo/test/login",
data: { email: $("#email").val(), Pwd: $("#pwd").val(), Code: $("#code").val() },
success: function (data) {
if (data.IsError) {
alert(data.ErrMsg);
} else {
//处理正常的业务
}
}
});
});