使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【四】——实现模型工厂,依赖注入以及格式配置
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html
前言
在上一篇中,我们已经初步开始使用Web Api了,但同时出现了一些很多不足之处,本章我们就着重来解决这些不足。
上篇导航:http://www.cnblogs.com/fzrain/p/3510035.html
配置JSON的格式
Web Api提供Xml和JSON作为返回数据的格式,框架会自动把这些格式注入管线。客户端可以通过Http请求头部来声明需要的数据格式,我们可以通过在“WebApiConfig”这个类来配置JSON数据的格式:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
首先根据HttpConfiguration对象获得jsonFormatter对象,然后设置ContractResolver属性。那么以后当我们使用JSON数据格式的时候就是“Camel”风格的了。
用Ninject实现依赖注入
如果读者是第一次接触依赖注入这个概念的话,可以参考:http://www.cnblogs.com/xray2005/archive/2009/07/28/1532908.html
OK,接下来我们就来实现依赖注入,在Controller文件夹中创建一个类“BaseApiController”继承自“APIController”。由于我们打算使用构造函数注入模式,因此它的构造函数接受一个ILearning类型的变量,下面上代码:
public class BaseApiController : ApiController
{
private ILearningRepository _repo; public BaseApiController(ILearningRepository repo)
{
_repo = repo;
} protected ILearningRepository TheRepository
{
get
{
return _repo;
}
}
}
将我们的“CoursesController”继承自“BaseApiController”,接下来就是使用Ninject框架来建立2者之间的关联:
首先使用NuGet来添加3个程序集:
- Ninject
- Ninject.Web.Common
- WebApiContrib.IoC.Ninject
添加好上述引用后,在APP_Start文件夹下就会出现一个类“NinjectWebCommon”,这个类就是在我们项目中配置依赖关系的。在之前的系列中,我们创建了“LearningRepository”,在它的构造函数中需要接受一个LearningContext对象(前篇导航:http://www.cnblogs.com/fzrain/p/3503952.html),因此我们也将这个依赖关系配置进来:
public static class NinjectWebCommon
{ private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>(); //Suport WebAPI Injection
GlobalConfiguration.Configuration.DependencyResolver = new WebApiContrib.IoC.Ninject.NinjectResolver(kernel); RegisterServices(kernel);
return kernel;
} private static void RegisterServices(IKernel kernel)
{
kernel.Bind<LearningContext>().To<LearningContext>().InRequestScope();
kernel.Bind<ILearningRepository>().To<LearningRepository>().InRequestScope();
}
}
我们使用了Ninject配置了Learningcontext对象,使得在http请求范围共用一个context对象,这么做对于创建复杂对象是非常好的。关于Ninject对象范围,可以参考:http://music.573114.com/Blog/Html/EB43/815024.html
实现模型工厂模式
模型工厂帮助我们创建需要响应给客户端的模型,因此我们将创建一些区别于领域模型(domain model)的新模型,新模型将与领域模型映射。例如:“Course”将映射到”courseModel”,”Tutor”将映射到“TutorModel“。同时应当考虑对象间的依赖关系。
为了实现这个功能,我们在”Model”文件夹中创建这几个类”SubjectModel“,”TutorModel“,”CourseModel“,”EnrollmentModel“,这些类就是一些简单的”POCO”类,用来响应给客户端的,下面上代码:
public class SubjectModel
{
public int Id { get; set; }
public string Name { get; set; }
} public class TutorModel
{
public int Id { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Data.Enums.Gender Gender { get; set; } } public class CourseModel
{
public int Id { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public double Duration { get; set; }
public string Description { get; set; }
public TutorModel Tutor { get; set; }
public SubjectModel Subject { get; set; } } public class EnrollmentModel
{
public DateTime EnrollmentDate { get; set; }
public CourseModel Course { get; set; }
}
有了这些响应给客户端的类,我们还需要一个创建这些类对象的工厂——”ModelFactory“:
public class ModelFactory
{
public ModelFactory()
{ } public CourseModel Create(Course course)
{
return new CourseModel()
{
Id = course.Id,
Name = course.Name,
Duration = course.Duration,
Description = course.Description,
Tutor = Create(course.CourseTutor),
Subject = Create(course.CourseSubject)
};
} public TutorModel Create(Tutor tutor)
{
return new TutorModel()
{
Id = tutor.Id,
Email = tutor.Email,
UserName = tutor.UserName,
FirstName = tutor.FirstName,
LastName = tutor.LastName,
Gender = tutor.Gender
};
} public SubjectModel Create(Subject subject)
{
return new SubjectModel()
{
Id = subject.Id,
Name = subject.Name
};
} public EnrollmentModel Create(Enrollment enrollment)
{
return new EnrollmentModel()
{
EnrollmentDate = enrollment.EnrollmentDate,
Course = Create(enrollment.Course)
};
}
}
我们做的很简单,重载了Create方法,传入领域模型即可创建我们响应给客户端的模型,在这里我们可以很轻易的控制对象间的依赖关系(CourseModel引用TutorModel,CourseModel引用SubjectModel)
到此为止我们已经解决了2个瑕疵:
(1)对象间的循环依赖
(2)控制了返回客户端的字段(Password不会响应给客户端了)
由于我们可能要在各个Controller中使用到ModelFactory对象,因此我们在BaseController中添加如下代码:
public class BaseApiController : ApiController
{
private ModelFactory _modelFactory; protected ModelFactory TheModelFactory
{
get
{
if (_modelFactory == null)
{
_modelFactory = new ModelFactory();
}
return _modelFactory;
}
}
}
在介绍”CoursesController”的变化之前,我们先解决一下之前提到的2个问题:
(1)对于每个资源返回一个URI
(2)对于单个资源返回一个Http响应码
为每个资源添加URI:
做法不复杂因为我们已经创建了模型工厂,举个简单的例子——如果我们要返回一个URI,要通过一下步骤:
1.给ModelFactory的构造函数传入一个”HttpRequestMessage“对象来创建”System.Web.Http.Routing.UrlHelper“对象,它会根据我们在WebApiConfig中配置的路由名字来构造URI
2.在”BaseApiController“中的”ModelFactory“构造函数中传入”System.Web.Http.Routing.UrlHelper“对象
3.在”CourseModel”中新增一个属性”URL“
public class ModelFactory
{
private System.Web.Http.Routing.UrlHelper _UrlHelper; public ModelFactory(HttpRequestMessage request)
{
_UrlHelper = new System.Web.Http.Routing.UrlHelper(request);
}
}
public class BaseApiController : ApiController
{
private ModelFactory _modelFactory; protected ModelFactory TheModelFactory
{
get
{
if (_modelFactory == null)
{
_modelFactory = new ModelFactory(Request);
}
return _modelFactory;
}
}
}
class ModelFactory
{
public CourseModel Create(Course course)
{
return new CourseModel()
{
Url = _UrlHelper.Link(“Courses”, new { id = course.Id }),
Id = course.Id,
/*Other CourseModel properties remain the same*/
};
}
关于模型工厂的更多介绍,可以参考:http://pluralsight.com/training/courses/TableOfContents?courseName=implementing-restful-aspdotnet-web-api(英文的,而且收费,唉。。 亚历山大)
为单个资源返回Http状态码:
Web Api框架中有一个”HttpResponseMessage“类可以用来返回Http状态码。有的时候使用状态码代替model来响应给客户端会更好,下面的例子就是在“Courses‘中的Getcourse(int id)方法中响应一个状态码。下面是我们最终修改后的CoursesController的代码:
public class CoursesController : BaseApiController
{
public CoursesController(ILearningRepository repo)
: base(repo)
{
} public IEnumerable<CourseModel> Get()
{
IQueryable<Course> query; query = TheRepository.GetAllCourses(); var results = query
.ToList()
.Select(s => TheModelFactory.Create(s)); return results;
} public HttpResponseMessage GetCourse(int id)
{
try
{
var course = TheRepository.GetCourse(id);
if (course != null)
{
return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(course));
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound);
} }
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
}
总结
到此为止我们总共完成了以下的改变:
1.将”ILearningRepository“注入到”CoursesController“的构造函数中
2.使用模型工厂模式创建了CourseModel以及关联属性ToturModel和SubjectModel
3.当资源没找到时我们返回404的状态码,发生异常我们返回400(badRequest)状态码,成功时返回200状态码
为了测试结果,我们发送一个Get请求(http://localhost:{your_port}/api/courses)在这里我们很好的控制了模型的返回字段,同时也解决了对象间循环依赖的问题:
[
{
"id": 1,
"url": "http://localhost:3300/api/courses/1",
"name": "History Teaching Methods 1",
"duration": 3,
"description": "The course will talk in depth about: History Teaching Methods 1",
"tutor": {
"id": 1,
"email": "Ahmad.Joudeh@outlook.com",
"userName": "AhmadJoudeh",
"firstName": "Ahmad",
"lastName": "Joudeh",
"gender": 0
},
"subject": {
"id": 1,
"name": "History"
}
},
{
"id": 2,
"url": "http://localhost:3300/api/courses/2",
"name": "History Teaching Methods 2",
"duration": 3,
"description": "The course will talk in depth about: History Teaching Methods 2",
"tutor": {
"id": 1,
"email": "Ahmad.Joudeh@outlook.com",
"userName": "AhmadJoudeh",
"firstName": "Ahmad",
"lastName": "Joudeh",
"gender": 0
},
"subject": {
"id": 1,
"name": "History"
}
},
下一章我们来解释Http方法(put,post,delete)。
本章代码:http://pan.baidu.com/s/1ntjq5Dn
使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【四】——实现模型工厂,依赖注入以及格式配置的更多相关文章
- ASP.NET Web Api构建基于REST风格的服务实战系列教程
使用ASP.NET Web Api构建基于REST风格的服务实战系列教程[十]——使用CacheCow和ETag缓存资源 系列导航地址http://www.cnblogs.com/fzrain/p/3 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【开篇】【持续更新中。。。】
最近发现web api很火,园内也有各种大神已经在研究,本人在asp.net官网上看到一个系列教程,原文地址:http://bitoftech.net/2013/11/25/detailed-tuto ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【九】——API变了,客户端怎么办?
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 一旦我们将API发布之后,消费者就会开始使用并和其他的一些数据混在一起.然而,当新的需求出现 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【八】——Web Api的安全性
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 这一篇文章我们主要来探讨一下Web Api的安全性,到目前为止所有的请求都是走的Http协议 ...
- [转]使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【八】——Web Api的安全性
本文转自:http://www.cnblogs.com/fzrain/p/3552423.html 系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html ...
- 使用ASP.NET WEB API构建基于REST风格的服务实战系列教程(一)——使用EF6构建数据库及模型
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 使用Entity Framework Code First模式构建数据库对象 已经决定使用EF C ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【二】——使用Repository模式构建数据库访问层
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 在数据访问层应用Repository模式来隔离对领域对象的细节操作是很有意义的.它位于映射层 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【五】——在Web Api中实现Http方法(Put,Post,Delete)
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 在Web Api中,我们对资源的CRUD操作都是通过相应的Http方法来实现——Post(新 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【外传】——Attribute Routing
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 题外话:由于这个技术点是新学的,并不属于原系列,但借助了原系列的项目背景,故命名外传系列,以后也可 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【三】——Web Api入门
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 经过前2节的介绍,我们已经把数据访问层搭建好了,从本章开始就是Web Api部分了.在正式开 ...
随机推荐
- Chrome 自动填充的表单是淡黄色的背景
Chrome 自动填充的表单是淡黄色的背景解决方案; input:-webkit-autofill { -webkit-box-shadow: 0 0 0px 1000px #fff inset; - ...
- HTML5存储
Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的.Cookie的大小是受限的,并且每次你请求一个新的页面的时候Cookie都会被发送过去,这样无形中浪费了带宽,另外co ...
- Android性能优化文章转载
今天看到几篇比较好的文章就转了!(链接如下) 转载注明出处:Sunzxyong Android性能优化之Bitmap的内存优化 Android性能优化之常见的内存泄漏 Android最佳实践之Syst ...
- 【BZOJ 1568】【JSOI 2008】Blue Mary开公司
经典的splay维护凸壳,但是看了看zky学长的题解最后决定写线段树维护标记永久化. Round1考到了这个之后一直没有理解标记永久化,CTSC也因为自己的缺陷丢掉了一些部分分,so sad 看来以后 ...
- 同 一个页面,不同请求路径,如何根据实际场景写JS
场景:使用同一个“添加群成员”的页面来操作 建群页面:建群成功后,返回查看群成员页面.在建群过程中直接添加群成员并返回一个群名称的参数. 添加群成员页面:在巳有群内添加群成员,添加成功后,返回查看群成 ...
- XCode显示iOS Simulators时不显示系统版本号并出现Identifier(UUID)
如果出现如下的显示问题,说明你添加了多个相同系统版本的iOS Simulators: 打开Windows->Devices,查看已经出现了相同版本的模拟器: 那么我们删除一些相同的模拟器,即可解 ...
- Android程序设计-圆形图片的实现
在android中,google只提供了对图形的圆形操作,而没有实现对图片的圆形操作,所以我们无法实现上述操作,在此我们将使用框架进行设计(下述框架为as编写): https://github.com ...
- 过滤器(Filter)的应用
过滤器的概念 Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应. 主要用于对HttpServletRequest 进行预处理,也可以对HttpSe ...
- 使用dnsmasq来提升CentOS上网速度
1. 安装dnsmasq dnsmasq的官方网址是:http://www.thekelleys.org.uk/dnsmasq/doc.html.利用里面的下载链接下载dnsmasq-2.72.tar ...
- 企业应用系统设计分享PPT
因今天上午需要为团队做一个分享,所以昨晚连夜写了一个<企业应用系统设计>的PPT,因为时间比较短,写的比较急.现在把PPT贴出来,做一个记录.同时也希望对大家有用. 文件我上传到了百度网盘 ...