1、 set; public string ApiUserName get; / .可以添加其他要专递的登录用户相关的信息 public abstract ResultObject Validate();ApiUserID和ApiUserName这样的字段是不需要客户端传递的,我们会根据登录人信息自动填充。根据实际中的经验,我们往往会做分页查询,会用到页码和每页条数,所为我们再定义个PageRequestBase: public abstract class PageRequestBase : UserRequestBase public int PageIndex get; public int
2、 PageSize get;因为.net只能继承单个父类,而且有些分页查询可能需要用户信息,所以我们选择继承UserRequestBase。当然,还可以根据自己的实际情况抽象出更多的公用类,在这不一一枚举。响应的设计分为两部分,第一个是实际响应部分,第二个会把响应包装一下,加上code和msg,用于表示调用状态和错误信息(好老的方法了,大家都懂)。响应接口IResponse里什么也没有,就是一个标记接口,不过我们也可以抽象出来两个常用的公用类用于响应列表和分页数据: public class ListResponseBase : IResponse public List List get;
3、public class PageResponseBase: ListResponseBase / / 页码数/summary / 总条数 public long TotalCount get; / 每页条数 / 总页数 public long PageCount get; 包装响应的时候,有两种情况,第一种是操作类接口,比如添加商品,这些接口是不用响应对象的,只要返回是否成功就行了,第二种查询类,这个时候必须要返回一些具体的东西了,所以响应包装设计成两个类:public class ResultObject / 等于0表示成功 public int Code get; / code不为0时,
4、返回错误消息 public string Msg get; public class ResultObject ResultObject where TResponse : public ResultObject() public ResultObject(TResponse data) Data = data; / 返回的数据 public TResponse Data get;IRequest接口的Validate()方法返回值就是第一个ResultObject,当请求参数验证不通过的时候,肯定是没有数据返回了。再业务逻辑层,我选择以包装类作为返回类型,因为有很多错误都会在业务逻辑层出现,
5、我们的接口是需要这些错误信息的。二、请求的Content-Type和模型绑定 现在前后端分离大行其道,我们做后端的通常会返回JSON格式给前端,响应的Content-Type为application/json,前端通过一些框架可以直接作为js对象使用。但是前端请求后端的时候还有很多是以form表单形式,也就是请求的Content-Type为:application/x-www-form-urlencoded,请求体为id=23&name=loogn这样的字符串,如果数据格式复杂了,前端不好传,后端解析起来也麻烦。还有的直接用一个固定参数传递json字符串,比如json=id:23,name:l
6、oogn,后端用formjson取出来后再反序列化。这些方法都可以,但是不够好,最好的方法是前端也直接传json,幸好现在很多web服务器都是支持请求的Content-Type为application/json的,这个时候请求的参数会以有效负荷(Payload)的形式传递过去,比如用jQuery的ajax来请求: $.ajax( type: POST, url:/product/editProduct contentType:application/json; charset=utf-8 data: JSON.stringify(id:1,name:name1), success: funct
7、ion (result) console.log(result); ) 除了contentType,还要注意使用了JSON.stringify把对象转换成了字符串。其实ajax使用的XmlHttpRequest对象只能处理字符串(json字符串呀,xml字符串呀,text纯文本呀,base64呀)。这些数据到了后端之后,从请求流里读出来就是json形式的字符串了,可直接反序列化成后端对象。然而这些考虑,.net mvc框架已经帮我们做好了,这都要归功于DefaultModelBinder。关于Form表单形式的请求,可以参见这位园友的文章:你从未知道如此强大的ASP.NET MVC Defau
8、ltModelBinder我这里想说的是,DefaultModelBinder足够智能,并不需要我们自己做什么,它会根据请求的contentType的不同,用不同的方式解析请求,然后绑定到对象,遇到contentType为application/json是,就直接反序列化得到对象,遇到application/x-www-form-urlencoded就用form表单的形式绑定对象,唯一要注意的就是前端同学,不要把请求的contentType和请求的实际内容搞错就行了。你告诉我你送过来一只猫,而实际上是一只狗,我以对待猫的方式对待狗当然就有被咬一口的危险了(肯定会报错)。三、自定义ApiResu
9、lt和ApiControllerBase因为我不需要RESTFul风格,也不需要根据客户端的意愿返回json或xml,所以我选择AsyncController作为控制器的基类。AsyncController是直接继承Controller的,而且支持异步处理,具体Controller和ApiController的区别,想了解的同学可以看这篇文章difference-between-apicontroller-and-controller-in-asp-net-mvc ,或者直接阅读源码。Controller里的Action需要返回一个ActionResult对象,结合上面的响应包装对象Resul
10、tObject,我决定自定义一个ApiResult作为Action的返回值,同时在这里处理jsonp调用、跨域调用、序列化的小驼峰命名和时间格式问题。 / api返回结果,控制jsonp、跨域、小驼峰命名和时间格式问题 public class ApiResult : ActionResult / 返回数据 public ResultObject ResultData get; / 返回数据编码,默认utf8 public Encoding ContentEncoding get; / 是否接受Get请求,默认允许 public JsonRequestBehavior JsonRequestB
11、ehavior get; / 是否允许跨域请求 public bool AllowCrossDomain get; / jsonp回调参数名 public string JsonpCallbackName = callback; public ApiResult() : this(null) public ApiResult(ResultObject resultData) this.ResultData = resultData; ContentEncoding = Encoding.UTF8; JsonRequestBehavior = JsonRequestBehavior.AllowG
12、et; AllowCrossDomain = true; public override void ExecuteResult(ControllerContext context) var response = context.HttpContext.Response; var request = context.HttpContext.Request; response.ContentEncoding = ContentEncoding; response.ContentType = text/plain if (ResultData != null) string buffer; if (
13、JsonRequestBehavior = JsonRequestBehavior.DenyGet) & string.Equals(context.HttpContext.Request.HttpMethod, GET) buffer = 该接口不允许Get请求 else var jsonpCallback = requestJsonpCallbackName; if (string.IsNullOrWhiteSpace(jsonpCallback) /如果可以跨域,写入响应头 if (AllowCrossDomain) WriteAllowAccessOrigin(context);app
14、lication/json buffer = JsonConvert.SerializeObject(ResultData, JsonSetting.Settings); /jsonp if (AllowCrossDomain) /这个判断可能非必须text/javascript buffer = string.Format(0(1);, jsonpCallback, JsonConvert.SerializeObject(ResultData, JsonSetting.Settings);该接口不允许跨域请求 try response.Write(buffer); catch (Except
15、ion exp) response.Write(exp.Message); response.Write(ApiResult.Data为null); response.End(); / 写入跨域请求头param name=context private void WriteAllowAccessOrigin(ControllerContext context) var origin = context.HttpContext.Request.HeadersOrigin; if (true) /可以维护一个允许跨域的域名集合,类判断是否可以跨域 context.HttpContext.Respo
16、nse.Headers.Add(Access-Control-Allow-Origin, origin ?*里面都是一些常规的逻辑,不做说明了,其中的JsonSetting就是设置序列化的小驼峰和日期格式的: public class JsonSetting public static JsonSerializerSettings Settings = new JsonSerializerSettings ContractResolver = new CamelCasePropertyNamesContractResolver(), DateFormatString = yyyy-MM-dd
17、HH:mm:ss ;这个时候有个问题,如果一个时间的字段需要yyyy-MM-dd这种格式怎么办呢?这个时候要定义一个JsonConverter的子类,来实现自定义日期格式: / 日期格式化器 public class CustomDateConverter : DateTimeConverterBase private IsoDateTimeConverter dtConverter = new IsoDateTimeConverter ; public CustomDateConverter(string format) dtConverter.DateTimeFormat = format
18、; public CustomDateConverter() : this() public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) return dtConverter.ReadJson(reader, objectType, existingValue, serializer); public override void WriteJson(JsonWriter writer, object value, Jso
19、nSerializer serializer) dtConverter.WriteJson(writer, value, serializer);在需要的响应属性上加上 JsonConverter(typeof(CustomDateConverter) 或 JsonConverter(typeof(CustomDateConverter),yyyy年MM月dd日) 即可。ApiResult定义好了,再定义一个控制器基类,目的是便于处理ApiResult: / API控制器基类 public class ApiControllerBase : AsyncController public Api
20、Result Api(TRequest request, Func handle) var requestBase = request as IRequest; if (requestBase ! /处理需要登录用户的请求 var userRequest = request as UserRequestBase; if (userRequest ! var loginUser = LoginUser.GetUser(); if (loginUser ! userRequest.ApiUserID = loginUser.UserID; userRequest.ApiUserName = log
21、inUser.UserName; var validResult = requestBase.Validate(); if (validResult ! return new ApiResult(validResult); var result = handle(request); /处理请求 return new ApiResult(result); /异常日志: return new ApiResult ResultData = new ResultObject Code = 1, Msg = 系统异常: + exp.Message ; public ApiResult Api(Func
22、var result = handle();/处理请求 /异常日志 / 异步apitypeparam name=TRequest/typeparamrequesthandlereturns/returns public Task ApiAsyncTRequest, Task return Api() = x.Result); );最常用的应该就是第一个Api方法,里面处理了请求参数的验证,把用户信息赋给需要的请求对象,异常记录等。第二个方法是对没有请求参数的api调用处理。第三个方法是异步处理,可以对异步IO处理做一些优化,比如你提供的这个接口是调用的另一个网络接口的情况。四、权限验证 关于这个问题,我在一篇文章中贴了一些代码,其实只要是知道怎么回事之后,自己可以想怎么玩就怎么玩了,下面讲的的没有涉及角色的权限。根据以往经验,我们可以把资源(也就是一个接口)的权限分为三个等级(标红的第二点很重要,会大大简化后台权限管理的工作):1,公开和访问2,登录用户可访问3,有权限的登录用户可访问所以我们如此设计验证的过滤器: public class AuthFilterAttribute : ActionFilterAttribute / 匿名可访问 /
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1