使用ASP.NET Web Api構建基於REST風格的服務實戰系列教程【四】——實現模型工廠,依賴注入以及格式配置

系列導航地址http://www.cnblogs.com/fzrain/p/3490137.htmlhtml

前言

在上一篇中,咱們已經初步開始使用Web Api了,但同時出現了一些不少不足之處,本章咱們就着重來解決這些不足。web

上篇導航:http://www.cnblogs.com/fzrain/p/3510035.htmljson

配置JSON的格式

Web Api提供Xml和JSON做爲返回數據的格式,框架會自動把這些格式注入管線。客戶端能夠經過Http請求頭部來聲明須要的數據格式,咱們能夠經過在「WebApiConfig」這個類來配置JSON數據的格式:api

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」風格的了。restful

用Ninject實現依賴注入

若是讀者是第一次接觸依賴注入這個概念的話,能夠參考:http://www.cnblogs.com/xray2005/archive/2009/07/28/1532908.htmlapp

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者之間的關聯:dom

首先使用NuGet來添加3個程序集:函數

  • Ninject
  • Ninject.Web.Common
  • WebApiContrib.IoC.Ninject

添加好上述引用後,在APP_Start文件夾下就會出現一個類「NinjectWebCommon」,這個類就是在咱們項目中配置依賴關係的。在以前的系列中,咱們建立了「LearningRepository」,在它的構造函數中須要接受一個LearningContext對象(前篇導航:http://www.cnblogs.com/fzrain/p/3503952.html),所以咱們也將這個依賴關係配置進來:post

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

相關文章
相關標籤/搜索