热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CRUD全栈式编程架构之控制器的设计

页面这里界面我采用jqueryminiui来做的,当你完全了解了整个设计之后可以轻松切换到其他的js框架,个人认为类似muniui,easyui等等这类可以将web界面做得和winform类似的框架
页面

这里界面我采用jquery miniui来做的,当你完全了解了整个设计之后可以轻松切换到其他的js框架,个人认为类似muniui,easyui等等这类可以将web界面做得和winform类似的框架,特别适合做后台管理系统。要讨论controller的设计必须结合界面,这里我给出界面截图和控制器的代码,这一篇主要讲控制器的代码,下一篇再讲界面的设计。

上一篇忘记说了,IVeiwModel是一个dto或者说viewmode的接口,我的应用里面一般不严格区分viewmode和dto,这个接口之后一个long Id的属性,

代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;
using Coralcode.Framework.Extensions;
using Coralcode.Framework.Log;
using Coralcode.Framework.Models;
using Coralcode.Framework.Mvc.Extensions;
using Coralcode.Framework.Mvc.Models.MiniUI;
using Coralcode.Framework.Mvc.Template;
using Coralcode.Framework.Services;
using Coralcode.Framework.Validator;
using Newtonsoft.Json;
using ViewType = Coralcode.Framework.Mvc.Models.MiniUI.ViewType;

namespace Coralcode.Framework.Mvc.ControlContent
{
public abstract class CrudCoralController : ContextCoralController
where TModel : class, IViewModel, new()
where TSearch : SearchBase, new()
where TOrder : OrderBase, new()
{
private readonly ICrudCoralService _service;
protected CrudCoralController(ICrudCoralService service)
{
_service = service;
}

protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
var routeValues = Request.GetRouteValues();
//页面的配置
ViewBag.Title = GetType().GetDescription();
ViewBag.EditUrl = Url.Action("AddEdit", routeValues);
ViewBag.DeleteUrl = Url.Action("BatchDelete", routeValues);
ViewBag.ListUrl = Url.Action("List", routeValues);
ViewBag.PageUrl = Url.Action("PageSearch", Request.GetRouteValues());
ViewBag.ShowPager = true;
}

[HttpGet]
public virtual ActionResult Index()
{
var viewModelType = typeof(TModel);
var properties =
viewModelType.GetProperties(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

var columns = new List();
foreach (var propertyInfo in properties)
{
var descAttr = propertyInfo.GetCustomAttribute();
if (descAttr == null) continue;

var column = descAttr.DataGridColumn;

if ((column.ViewType & ViewType.List) == 0)
{
continue;
}


if (string.IsNullOrWhiteSpace(column.Field))
{
column.Field = propertyInfo.Name;
}

columns.Add(column);

var dateType = propertyInfo.GetCustomAttribute();

if (dateType == null)
continue;
switch (dateType.DataType)
{
case DataType.Custom:
break;
case DataType.DateTime:
column.Renderer = "onDateTimeRenderer";
break;
case DataType.Date:
column.Renderer = "onDateRenderer";
break;
case DataType.Time:
column.Renderer = "onTimeRenderer";
break;
case DataType.Duration:
break;
case DataType.PhoneNumber:
break;
case DataType.Currency:
break;
case DataType.Text:
break;
case DataType.Html:
break;
case DataType.MultilineText:
break;
case DataType.EmailAddress:
break;
case DataType.Password:
break;
case DataType.Url:
break;
case DataType.ImageUrl:
break;
case DataType.CreditCard:
break;
case DataType.PostalCode:
break;
case DataType.Upload:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
ViewBag.Header = columns;

ListBindData();

return View(new TSearch());
}

[HttpPost]
public virtual JsonResult List(TSearch search)
{
return ToJson(_service.Search(search));
}

[HttpPost]
public virtual JsonResult PageSearch(TSearch search,PageInfo page,TOrder order)
{
return ToJson(_service.PageSearch(page,search, order));
}

///
/// 这里用来做编辑
///

///
///
[HttpGet]
public virtual ActionResult AddEdit(long? id)
{
TModel model = id.HasValue ? _service.Get(id.Value) : new TModel();
AddEditBindData(model);
return View(model);
}

[ValidateInput(false)]
[HttpPost]
public virtual JsonResult AddEdit(TModel model)
{
try
{
if (!ModelState.IsValid)
return AjaxErrorResult(ModelState.GetErroreMessage());
var ajaxMessage = ValidateAndPreProccess(model);
if (ajaxMessage.State != ResultState.Success)
{
return AjaxErrorResult(ajaxMessage.Message);
}

if (model.Id <1)
_service.Create(model);
else
_service.Modify(model);
}
catch (Exception ex)
{
LoggerFactory.Instance.Error("{0}编辑产生错误;数据:{1}", ex, typeof(TModel),
JsonConvert.SerializeObject(model ?? new TModel()));
return AjaxExceptionResult(ex);
}
return AjaxOkResult();
}

///
/// 业务验证
/// 比如用户名唯一
///

///
///
protected virtual ResultMessage ValidateAndPreProccess(TModel model)
{
if (!EntityValidatorProvider.Validator.IsValid(model))
return new ResultMessage
{
State = ResultState.Fail,
Message = string.Join(";
", EntityValidatorProvider.Validator.GetInvalidMessages(model))
};

return new ResultMessage
{
State = ResultState.Success
};
}

protected virtual void ListBindData()
{

}

///
/// 这里用来扩展绑定数据
///

protected virtual void AddEditBindData(TModel model)
{
//这里用来做数据绑定的操作
}

[HttpPost]
public virtual JsonResult Delete(long id)
{
return this.BatchDelete(id.ToString());
}

[HttpPost]
public virtual JsonResult BatchDelete(string ids)
{
if (string.IsNullOrWhiteSpace(ids)) return AjaxErrorResult("参数不能为空");
try
{
var idList = ids.Split(',').ToList().ConvertAll(Convert.ToInt64);
if (idList.Count <1) return AjaxErrorResult("参数不能为空");
_service.Remove(idList);
}
catch (Exception ex)
{
LoggerFactory.Instance.Error("{0}编辑产生错误;数据:{1}", ex, typeof(TModel), JsonConvert.SerializeObject(ids));
return AjaxExceptionResult(ex);
}
return AjaxOkResult();
}
}


public abstract class CrudCoralController : CrudCoralController
where TModel : class, IViewModel, new()
where TSearch : SearchBase, new()
{
protected CrudCoralController(ICrudCoralService service) : base(service)
{
}
}


public abstract class CrudCoralController : CrudCoralController
where TModel : class, IViewModel, new()
{
protected CrudCoralController(ICrudCoralService service)
: base(service)
{
}
}


}

  

操作

页面配置主要是在Controler.Initialize 方法中配置的。这里有个地方注意,在生成url的地方一定要带上routedata,这个可以充分利用mvc自带valueprovider的设计,结合菜单url可以为查询和模型的绑定提供值绑定。这部分会在完整demo放出后再详细说明。

新增/编辑

这里我把新增和编辑作为一个来对待,大部分情况都是这样,当id为0时候认为是新增,当id不为0的时候是编辑。当然如果你想分开只需添加一个配置和一个方法并结合界面js,
扩展必须是允许的

删除

这里我只使用了批量删除的方法,界面上应该给出的是checkbox,选择之后点击删除即可.这里注意界面传输过来的数据是“,”分开的字符串,解析之后做删除操作。

查询

查询提供了未分页和分页两种。分页的话搭配showpager来配置。大部分后台列表都需要分页,但是如果结合三级菜单,结合url中routedata,可以将数据进行一定划分,数据量不大的情况下不用分页用list也一样方便。这里界面上我没有排序的功能,后面我整理demo的时候再给出排序吧。

列表

数据绑定

在index方法中我们首先取出模型的元数据,那些列需要绑定到界面,数据类型是什么,并且可以定义一些一些元。例如高度,宽度,当然最好是搭配界面自适应使用。另外给出listbindata这个没有实现的方法,主要是给查询时候绑定数据用的。例如你查询实体中有类型combox,可以获取到类型用ViewBag传递到界面使用。另外还可以给界面一些默认值,例如你查询实体里面有开始时间,结束时间,也可以在这里给出配置。当然如果你查询实体只是独享只提供一个表查询,那么最好是写在构造函数中。

分页

分页和查询只多了一个pageinfo(里面只包含当前页和行数)
ps:mvc的绑定机制会会导致无法赋值,或者复制错乱问题
>* pageinfo的属性名称不要和查询实体的属性名称冲突,除非是业务需要。 
>* 另外一定不要再查询实体中定义类似page,search,order属性名。
>* 如area,controller,action的作为入参或者入参的属性也不要有

新增和编辑

区分和绑定

编辑通过id是否有值来区分,如果有值,那么会使用服务查询到数据绑定到界面,没有的话会new一个出来。AddEditBindData 方法是为页面绑定提供数据源,例如编辑界面有combox这类可以更方便绑定。有人会说可以用界面ajax无刷新绑定,我建议是尽量不要这么做,如果界面有几十个combox这里很容易导致大量的ajax请求,我称之为ajax灾难,所以我这里会强烈建议使用者使用后台提供数据绑定,避免上述问题。

验证和预处理

提交数据之后首先做的事mvc自带的验证,这里做了一个小封装,可以取出所有错误返回到界面,不过最佳方式是,界面mvc客户端js做绑定工作。具体可以查看artechmvc讲解中的方式。另外这里做了自定义的验证。这里把方法名叫ValidateAndPreProccess,因为有时候我们需要对传输过来的数据做一部分预处理,比如ids这种有可能你服务需要的是一个list,这时候就可以做一个转换了。另外验证和预处理逻辑很难区分出是那个先做哪个后做,所以这里将两个方法合并。预处理中调用了自定义验证,这里用的NlayerApp的验证方式,具体请自行搜索,其实这里并没有卵用。mvc的验证比这个做得好,但是后面做导入导出的时候会用到暂且就放这里吧,结合导入导出再说这部分。

  public static class MvcExtensions
{
public static string GetErroreMessage(this ModelStateDictionary state)
{
return string.Join( Environment.NewLine,state.SelectMany(item => item.Value.Errors)
.Select(item => item.ErrorMessage)
.Where(item => !string.IsNullOrWhiteSpace(item)));
}
}

 

其他

AjaxXXXResult

分为成功,失败和部分成功,部分成功会在导入数据时候用到。在扩展控制器的顶层基类提供更多的方法也是做应用时候必须的,就算你啥都没有也建议你先占个位置.

using System;
using System.Web.Mvc;
using Coralcode.Framework.Models;
using Coralcode.Framework.Mvc.ActionResults;

namespace Coralcode.Framework.Mvc.ControlContent
{
public class CoralController : Controller
{
protected JsonResult AjaxOkResult(object data = null, string message = "success")
{
var result = new ResultMessage
{
State = ResultState.Success,
Message = message,
Data = data,
};
return ToJson(result, JsonRequestBehavior.AllowGet);
}
protected JsonResult AjaxExceptionResult(Exception ex, object data = null)
{
var result = new ResultMessage
{
State = ResultState.Fail,
Message = ex.ToString(),
Data = data,
};

return ToJson(result, JsonRequestBehavior.AllowGet);
}
protected JsonResult AjaxErrorResult(object data = null, string message = "fail")
{
var result = new ResultMessage
{
State = ResultState.Fail,
Message = message,
Data = data,
};
return ToJson(result, JsonRequestBehavior.AllowGet);
}
protected JsonResult AjaxPartSuccessResult(object data = null, string message = "partsuccess")
{
var result = new ResultMessage
{
State = ResultState.PartSuccess,
Message = message,
Data = data,
};

return ToJson(result, JsonRequestBehavior.AllowGet);
}
protected JsonResult ToJson(object obj, JsonRequestBehavior behavior = JsonRequestBehavior.AllowGet)
{
return new CustomJsonResult()
{
Data = obj,
JsOnRequestBehavior= behavior
};


}

}
}

 

ResultMessage

这里泛型,主要是用在服务端调用api时候数据转换类型更方便,所有的我ajax请求都做了类似封装,这里会在和mvc结合时候重点说明。

namespace Coralcode.Framework.Models
{
public class BaseMessage
{
public BaseMessage() { }
public BaseMessage(ResultState state,string message="" )
{
State = state;
Message = message;
}

public ResultState State { get; set; }
public string Message { get; set; }
}

public class ResultMessage : BaseMessage
{
public ResultMessage() { }
public ResultMessage(ResultState state, string message = "",object data=null)
: base(state, message)
{
Data = data;
}
public object Data { get; set; }

}

public class ResultMessage : BaseMessage
{
public ResultMessage() { }
public T Data { get; set; }

public ResultMessage(ResultState state, string message = "",T data=default(T))
: base(state, message)
{
Data = data;
}
}

public enum ResultState
{
Success,
Fail,
PartSuccess,
}
}

 

CustomJsonResult

这里对JsonResult 做了扩展,性能更快,并且解决long类型数据返回到界面数据最后两位丢失的问题。

using System;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Coralcode.Framework.Mvc.ActionResults
{
public class CustomJsonResult : JsonResult
{

public override void ExecuteResult(ControllerContext context)
{
if (cOntext== null)
{
throw new ArgumentNullException("context");
}
if (JsOnRequestBehavior== JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException();
}

HttpResponseBase respOnse= context.HttpContext.Response;

if (!String.IsNullOrEmpty(ContentType))
{
response.COntentType= ContentType;
}
else
{
response.COntentType= "application/json";
}
if (ContentEncoding != null)
{
response.COntentEncoding= ContentEncoding;
}
if (Data != null)
{
response.Write(JsonConvert.SerializeObject(Data, new IdToStringConverter()));
}
}
}

public class IdToStringConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken jt = JValue.ReadFrom(reader);

return jt.Value();
}

public override bool CanConvert(Type objectType)
{
return typeof(Int64) == objectType;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.ToString());
}
}

}

 PS:
  这个js精度丢失的问题是由于js本身设计时候一个bug。这里刚开始遇到也困扰很久,一直用类中一个字符串属性取代。但是后来狠下心,还是从mvc底层去解决问题。有问题不要绕,就是干不要怂-_-!

  这里注意JsonRequestBehavior.AllowGet ,面试提问利器-_-!

 

PS:注意我所有的ps,都是踩过的坑,并且是不容易发现的坑。

  

 


推荐阅读
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 本文介绍了ASP.NET Core MVC的入门及基础使用教程,根据微软的文档学习,建议阅读英文文档以便更好理解,微软的工具化使用方便且开发速度快。通过vs2017新建项目,可以创建一个基础的ASP.NET网站,也可以实现动态网站开发。ASP.NET MVC框架及其工具简化了开发过程,包括建立业务的数据模型和控制器等步骤。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
author-avatar
芸阁__907
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有