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

简介: 原文:使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【四】——实现模型工厂,依赖注入以及格式配置系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 在上一篇中,我们已经初步开始使用Web Api了,但同时出现了一些很多不足之处,本章我们就着重来解决这些不足。
原文: 使用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

目录
相关文章
|
25天前
|
API 数据库 数据安全/隐私保护
利用Django框架构建高效后端API服务
本文将介绍如何利用Django框架构建高效的后端API服务。通过深入分析Django框架的特性和优势,结合实际案例,探讨了如何利用Django提供的强大功能来构建高性能、可扩展的后端服务。同时,还对Django框架在后端开发中的一些常见问题进行了解决方案的探讨,并提出了一些建设性的建议。
40 3
|
26天前
|
网络协议 Java Nacos
nacos常见问题之在web界面 上下线服务时报错 400如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
29 0
|
26天前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
329 3
|
1月前
|
JSON API 数据库
解释如何在 Python 中实现 Web 服务(RESTful API)。
解释如何在 Python 中实现 Web 服务(RESTful API)。
23 0
|
4天前
|
监控 JavaScript 安全
构建微服务架构下的API网关
【4月更文挑战第15天】在微服务架构中,API网关扮演着至关重要的角色。它作为系统的唯一入口,不仅负责请求的路由、负载均衡和认证授权,还涉及到监控、日志记录和服务熔断等关键功能。本文将探讨如何构建一个高效且可靠的API网关,涵盖其设计原则、核心组件以及实现策略,旨在为后端开发人员提供一套实用的指导方案。
18 4
|
16天前
|
前端开发 Java API
构建RESTful API:Java中的RESTful服务开发
【4月更文挑战第3天】本文介绍了在Java环境中构建RESTful API的重要性及方法。遵循REST原则,利用HTTP方法处理资源,实现CRUD操作。在Java中,常用框架如Spring MVC简化了RESTful服务开发,包括定义资源、设计表示层、实现CRUD、考虑安全性、文档和测试。通过Spring MVC示例展示了创建RESTful服务的步骤,强调了其在现代Web服务开发中的关键角色,有助于提升互操作性和用户体验。
构建RESTful API:Java中的RESTful服务开发
|
17天前
|
数据采集 Java API
python并发编程: Python使用线程池在Web服务中实现加速
python并发编程: Python使用线程池在Web服务中实现加速
17 3
python并发编程: Python使用线程池在Web服务中实现加速
|
22天前
|
安全 API 开发者
构建高效可扩展的RESTful API服务
在数字化转型的浪潮中,构建一个高效、可扩展且易于维护的后端API服务是企业竞争力的关键。本文将深入探讨如何利用现代后端技术栈实现RESTful API服务的优化,包括代码结构设计、性能调优、安全性强化以及微服务架构的应用。我们将通过实践案例分析,揭示后端开发的最佳实践,帮助开发者提升系统的响应速度和处理能力,同时确保服务的高可用性和安全。
25 3
|
28天前
|
缓存 负载均衡 监控
构建高效微服务架构:API网关的作用与实践
【2月更文挑战第31天】 在当今的软件开发领域,微服务架构已成为实现系统高度模块化和易于扩展的首选方法。然而,随着微服务数量的增加,确保通信效率和管理一致性变得尤为重要。本文将探讨API网关在微服务架构中的核心角色,包括其在请求路由、安全性、负载均衡以及聚合功能方面的重要性。我们将通过具体案例分析,展示如何利用API网关优化后端服务,并讨论实施过程中的最佳实践和常见挑战。
|
28天前
|
安全 API 数据安全/隐私保护
email api接口配置教程步骤详解
Email API是用于程序化访问邮件服务的工具,让开发者能集成邮件功能到应用中。配置Email API包括选择供应商(如SendGrid、Mailgun、AokSend),注册获取API密钥,配置API参数,及测试邮件发送。使用Email API能提升邮件发送的可靠性和效率,便于邮件管理及营销活动。AokSend支持大量验证码发送,适合高效邮件运营。