系列导航地址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数据的格式:

  1. public static class WebApiConfig
  2. {
  3. public static void Register(HttpConfiguration config)
  4. {
  5. var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
  1. jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  1. }
  2. }

首先根据HttpConfiguration对象获得jsonFormatter对象,然后设置ContractResolver属性。那么以后当我们使用JSON数据格式的时候就是“Camel”风格的了。

用Ninject实现依赖注入

如果读者是第一次接触依赖注入这个概念的话,可以参考:http://www.cnblogs.com/xray2005/archive/2009/07/28/1532908.html

OK,接下来我们就来实现依赖注入,在Controller文件夹中创建一个类“BaseApiController”继承自“APIController”。由于我们打算使用构造函数注入模式,因此它的构造函数接受一个ILearning类型的变量,下面上代码:

  1. public class BaseApiController : ApiController
  2. {
  3. private ILearningRepository _repo;
  4.  
  5. public BaseApiController(ILearningRepository repo)
  6. {
  7. _repo = repo;
  8. }
  9.  
  10. protected ILearningRepository TheRepository
  11. {
  12. get
  13. {
  14. return _repo;
  15. }
  16. }
  17. }

将我们的“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),因此我们也将这个依赖关系配置进来:

  1. public static class NinjectWebCommon
  2. {
  3.  
  4. private static IKernel CreateKernel()
  5. {
  6. var kernel = new StandardKernel();
  7. kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
  8. kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
  9.  
  10. //Suport WebAPI Injection
  11. GlobalConfiguration.Configuration.DependencyResolver = new WebApiContrib.IoC.Ninject.NinjectResolver(kernel);
  12.  
  13. RegisterServices(kernel);
  14. return kernel;
  15. }
  16.  
  17. private static void RegisterServices(IKernel kernel)
  18. {
  19. kernel.Bind<LearningContext>().To<LearningContext>().InRequestScope();
  20. kernel.Bind<ILearningRepository>().To<LearningRepository>().InRequestScope();
  21. }
  22. }

我们使用了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”类,用来响应给客户端的,下面上代码:

  1. public class SubjectModel
  2. {
  3. public int Id { get; set; }
  4. public string Name { get; set; }
  5. }
  6.  
  7. public class TutorModel
  8. {
  9. public int Id { get; set; }
  10. public string Email { get; set; }
  11. public string UserName { get; set; }
  12. public string FirstName { get; set; }
  13. public string LastName { get; set; }
  14. public Data.Enums.Gender Gender { get; set; }
  15.  
  16. }
  17.  
  18. public class CourseModel
  19. {
  20. public int Id { get; set; }
  21. public string Url { get; set; }
  22. public string Name { get; set; }
  23. public double Duration { get; set; }
  24. public string Description { get; set; }
  25. public TutorModel Tutor { get; set; }
  26. public SubjectModel Subject { get; set; }
  27.  
  28. }
  29.  
  30. public class EnrollmentModel
  31. {
  32. public DateTime EnrollmentDate { get; set; }
  33. public CourseModel Course { get; set; }
  34. }

有了这些响应给客户端的类,我们还需要一个创建这些类对象的工厂——”ModelFactory“:

  1. public class ModelFactory
  2. {
  3. public ModelFactory()
  4. {
  5.  
  6. }
  7.  
  8. public CourseModel Create(Course course)
  9. {
  10. return new CourseModel()
  11. {
  12. Id = course.Id,
  13. Name = course.Name,
  14. Duration = course.Duration,
  15. Description = course.Description,
  16. Tutor = Create(course.CourseTutor),
  17. Subject = Create(course.CourseSubject)
  18. };
  19. }
  20.  
  21. public TutorModel Create(Tutor tutor)
  22. {
  23. return new TutorModel()
  24. {
  25. Id = tutor.Id,
  26. Email = tutor.Email,
  27. UserName = tutor.UserName,
  28. FirstName = tutor.FirstName,
  29. LastName = tutor.LastName,
  30. Gender = tutor.Gender
  31. };
  32. }
  33.  
  34. public SubjectModel Create(Subject subject)
  35. {
  36. return new SubjectModel()
  37. {
  38. Id = subject.Id,
  39. Name = subject.Name
  40. };
  41. }
  42.  
  43. public EnrollmentModel Create(Enrollment enrollment)
  44. {
  45. return new EnrollmentModel()
  46. {
  47. EnrollmentDate = enrollment.EnrollmentDate,
  48. Course = Create(enrollment.Course)
  49. };
  50. }
  51. }

我们做的很简单,重载了Create方法,传入领域模型即可创建我们响应给客户端的模型,在这里我们可以很轻易的控制对象间的依赖关系(CourseModel引用TutorModel,CourseModel引用SubjectModel)

到此为止我们已经解决了2个瑕疵:

(1)对象间的循环依赖

(2)控制了返回客户端的字段(Password不会响应给客户端了)

由于我们可能要在各个Controller中使用到ModelFactory对象,因此我们在BaseController中添加如下代码:

  1. public class BaseApiController : ApiController
  2. {
  3. private ModelFactory _modelFactory;
  4.  
  5. protected ModelFactory TheModelFactory
  6. {
  7. get
  8. {
  9. if (_modelFactory == null)
  10. {
  11. _modelFactory = new ModelFactory();
  12. }
  13. return _modelFactory;
  14. }
  15. }
  16. }

在介绍”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“

  1. public class ModelFactory
  2. {
  3. private System.Web.Http.Routing.UrlHelper _UrlHelper;
  4.  
  5. public ModelFactory(HttpRequestMessage request)
  6. {
  7. _UrlHelper = new System.Web.Http.Routing.UrlHelper(request);
  8. }
  9. }
  1. public class BaseApiController : ApiController
  2. {
  3. private ModelFactory _modelFactory;
  4.  
  5. protected ModelFactory TheModelFactory
  6. {
  7. get
  8. {
  9. if (_modelFactory == null)
  10. {
  11. _modelFactory = new ModelFactory(Request);
  12. }
  13. return _modelFactory;
  14. }
  15. }
  16. }
  1. class ModelFactory
  2. {
  3. public CourseModel Create(Course course)
  4. {
  5. return new CourseModel()
  6. {
  7. Url = _UrlHelper.Link(“Courses”, new { id = course.Id }),
  8. Id = course.Id,
  9. /*Other CourseModel properties remain the same*/
  10. };
  11. }

关于模型工厂的更多介绍,可以参考:http://pluralsight.com/training/courses/TableOfContents?courseName=implementing-restful-aspdotnet-web-api(英文的,而且收费,唉。。 亚历山大)

为单个资源返回Http状态码:

Web Api框架中有一个”HttpResponseMessage“类可以用来返回Http状态码。有的时候使用状态码代替model来响应给客户端会更好,下面的例子就是在“Courses‘中的Getcourse(int id)方法中响应一个状态码。下面是我们最终修改后的CoursesController的代码:

  1. public class CoursesController : BaseApiController
  2. {
  3. public CoursesController(ILearningRepository repo)
  4. : base(repo)
  5. {
  6. }
  7.  
  8. public IEnumerable<CourseModel> Get()
  9. {
  10. IQueryable<Course> query;
  11.  
  12. query = TheRepository.GetAllCourses();
  13.  
  14. var results = query
  15. .ToList()
  16. .Select(s => TheModelFactory.Create(s));
  17.  
  18. return results;
  19. }
  20.  
  21. public HttpResponseMessage GetCourse(int id)
  22. {
  23. try
  24. {
  25. var course = TheRepository.GetCourse(id);
  26. if (course != null)
  27. {
  28. return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(course));
  29. }
  30. else
  31. {
  32. return Request.CreateResponse(HttpStatusCode.NotFound);
  33. }
  34.  
  35. }
  36. catch (Exception ex)
  37. {
  38. return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
  39. }
  40. }
  41. }

总结

到此为止我们总共完成了以下的改变:

1.将”ILearningRepository“注入到”CoursesController“的构造函数中

2.使用模型工厂模式创建了CourseModel以及关联属性ToturModel和SubjectModel

3.当资源没找到时我们返回404的状态码,发生异常我们返回400(badRequest)状态码,成功时返回200状态码

为了测试结果,我们发送一个Get请求(http://localhost:{your_port}/api/courses)在这里我们很好的控制了模型的返回字段,同时也解决了对象间循环依赖的问题:

  1. [
  2. {
  3. "id": 1,
  4. "url": "http://localhost:3300/api/courses/1",
  5. "name": "History Teaching Methods 1",
  6. "duration": 3,
  7. "description": "The course will talk in depth about: History Teaching Methods 1",
  8. "tutor": {
  9. "id": 1,
  10. "email": "Ahmad.Joudeh@outlook.com",
  11. "userName": "AhmadJoudeh",
  12. "firstName": "Ahmad",
  13. "lastName": "Joudeh",
  14. "gender": 0
  15. },
  16. "subject": {
  17. "id": 1,
  18. "name": "History"
  19. }
  20. },
  21. {
  22. "id": 2,
  23. "url": "http://localhost:3300/api/courses/2",
  24. "name": "History Teaching Methods 2",
  25. "duration": 3,
  26. "description": "The course will talk in depth about: History Teaching Methods 2",
  27. "tutor": {
  28. "id": 1,
  29. "email": "Ahmad.Joudeh@outlook.com",
  30. "userName": "AhmadJoudeh",
  31. "firstName": "Ahmad",
  32. "lastName": "Joudeh",
  33. "gender": 0
  34. },
  35. "subject": {
  36. "id": 1,
  37. "name": "History"
  38. }
  39. },

下一章我们来解释Http方法(put,post,delete)。

本章代码:http://pan.baidu.com/s/1ntjq5Dn

使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【四】——实现模型工厂,依赖注入以及格式配置的更多相关文章

  1. ASP.NET Web Api构建基于REST风格的服务实战系列教程

    使用ASP.NET Web Api构建基于REST风格的服务实战系列教程[十]——使用CacheCow和ETag缓存资源 系列导航地址http://www.cnblogs.com/fzrain/p/3 ...

  2. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【开篇】【持续更新中。。。】

    最近发现web api很火,园内也有各种大神已经在研究,本人在asp.net官网上看到一个系列教程,原文地址:http://bitoftech.net/2013/11/25/detailed-tuto ...

  3. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【九】——API变了,客户端怎么办?

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 一旦我们将API发布之后,消费者就会开始使用并和其他的一些数据混在一起.然而,当新的需求出现 ...

  4. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【八】——Web Api的安全性

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 这一篇文章我们主要来探讨一下Web Api的安全性,到目前为止所有的请求都是走的Http协议 ...

  5. [转]使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【八】——Web Api的安全性

    本文转自:http://www.cnblogs.com/fzrain/p/3552423.html 系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html ...

  6. 使用ASP.NET WEB API构建基于REST风格的服务实战系列教程(一)——使用EF6构建数据库及模型

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 使用Entity Framework Code First模式构建数据库对象 已经决定使用EF C ...

  7. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【二】——使用Repository模式构建数据库访问层

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 在数据访问层应用Repository模式来隔离对领域对象的细节操作是很有意义的.它位于映射层 ...

  8. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【五】——在Web Api中实现Http方法(Put,Post,Delete)

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 在Web Api中,我们对资源的CRUD操作都是通过相应的Http方法来实现——Post(新 ...

  9. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【外传】——Attribute Routing

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 题外话:由于这个技术点是新学的,并不属于原系列,但借助了原系列的项目背景,故命名外传系列,以后也可 ...

  10. 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【三】——Web Api入门

    系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 经过前2节的介绍,我们已经把数据访问层搭建好了,从本章开始就是Web Api部分了.在正式开 ...

随机推荐

  1. Install marvel and head plugin for ealsticsearch

    安装ES插件 marvel marvel是ES的供开发者免费使用的管理工具,他内置了一款叫做Sense的控制台,Sense是运行在浏览器中的,基于Sense可以很方便的和ES进行通讯.官方文档中的很多 ...

  2. iOS开发,应用间的跳转

    预习:URL由两部分组成-- 1.scheme:协议头(http://  ftp:// 等等) 2.path:路径(URL中path可以没有) 一.简单实现跳转到指定APP(也就是说跳转到的APP必须 ...

  3. 【CodeVS 3290】【NOIP 2013】华容道

    http://codevs.cn/problem/3290/ 据说2013年的noip非常难,但Purpleslz学长还是AK了.能A掉这道题真心orz. 设状态$(i,j,k)$表示目标棋子在$(i ...

  4. ubuntu15.10下搭建cordova+ionic开发环境

    安装jdk 在命令下输入java如果没有安装会提示该命令包含于openjdk软件包 sudo apt-get install openjdk然后按下tab会列出openjdk开头的软件包 我这里就选择 ...

  5. 关于Yii2中CSS,JS文件的引入心得

    js和css的引入 use yii\helpers\Html; 1.全局引入,所有的view生效 /assets/AppAsset.php public $css = [ 'css/site.css' ...

  6. 回合对战制游戏第一篇(初识java)

    回合对战制游戏第一篇 一,所谓的java. java是一门完全面向对象的编程语言,而之前所接触到的C语言是一门面向有一个过程的语音,对于两个的区别应该有一个清楚的认识. java的第一个内容. 类和对 ...

  7. 【bzoj2118】 墨墨的等式

    http://www.lydsy.com/JudgeOnline/problem.php?id=2118 (题目链接) 题意 给出${B}$的取值范围${[Bmin,Bmax]}$,求方程${a_{1 ...

  8. dedecms /include/helpers/archive.helper.php SQL Injection Vul

    catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 Dedecms会员中心注入漏洞 Relevant Link: http: ...

  9. GCD XOR, ACM/ICPC Dhaka 2013, UVa12716

    不同的枚举方法,效率完全不同.值得记录一下! #include <cstdio> #include <cstring> , count = ; ]; void pre() { ...

  10. js实现A*寻路算法

    这两天在做百度前端技术学院的题目,其中有涉及到寻路相关的,于是就找来相关博客进行阅读. 看了Create Chen写的理解A*寻路算法具体过程之后,我很快就理解A*算法的原理.不得不说作者写的很好,通 ...