65.9K
CodeProject 正在变化。 阅读更多。
Home

以思维的速度构建和部署 ASP.NET Web API 2 服务层!

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (15投票s)

2014年2月4日

CPOL

4分钟阅读

viewsIcon

111232

downloadIcon

1192

学习如何使用一个简单的示例开发 WebAPI 服务层

下载源代码 

引言

最近我有一个项目需要使用 Web API 2。需求是将一个快速粗糙的 Beta 版本部署到生产环境,然后进行迭代,修复 bug 和数据问题。我一直在寻找一篇能帮助我将项目从开发部署到生产的文章,但一直找不到。本文的目的是尝试在 60 分钟内将一个简单的 Web API 2 服务部署到生产环境。我强烈建议您参考快速参考部分,以深入了解 Web API 和所有细节。

先决条件

  1. Visual Studio 2013
  2. 使用 SOAP UI 进行单元测试
  3. 了解如何使用 NuGet 包来更新您的解决方案
  4. 愿意解决所有的技术问题

快速参考

(为深入理解必读,但本文不要求)

首先 

第一步:启动 Visual Studio 2013。从“文件”菜单中,选择“新建”,然后选择“项目”。

在“模板”窗格中,选择“已安装模板”,展开“Visual C#”节点。在“Visual C#”下,选择“Web”,选择“Visual Studio 2012”。在项目模板列表中,选择“ASP.NET MVC 4 Web Application”。

将项目命名为“VehicleStore”。请参见下面的屏幕截图。

 

在“新建 ASP.NET MVC 4 项目”对话框中,选择“Web API”模板。选中“创建单元测试项目”复选框。单击“确定”。请参见下面的屏幕截图。

 

第二步:管理 NuGet 包。从“工具”菜单中,选择“NuGet 包管理器”,然后选择“使用解决方案管理 NuGet 包...”。要了解更多关于 NuGet 的信息,请参考此链接:安装 NuGet。安装所有 ASP.NET Web API 2.1 NuGet 包,如下图所示。 

配置文件

第三步:Global.asax

Application_Start() 方法中添加以下行以启用 Web API 2。

            GlobalConfiguration.Configure(WebApiConfig.Register);

            // NOTE: This commented code will NOT work for Web API 2.0.
            //WebApiConfig.Register(GlobalConfiguration.Configuration);
第四步:WebApiConfig.cs

Register(HttpConfiguration config) 方法中添加以下行以启用 Web API 2。

   
            // Add this line for Web API 2 - Attribute Routing
            config.MapHttpAttributeRoutes();

构建数据类和领域特定模型

第五步:创建一个 VehicleInfo 数据类。

  
 
namespace VehicleStore.Models
{
    /// 
    /// A Vehicle Info data class.
    /// 
    public class VehicleInfo
    {
        public Guid Id { get; set; }
        public string Trim { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
        public int Year { get; set; }
        public System.Drawing.Color Color { get; set; }
        public string VIN { get; set; }
        public int SafetyRating { get; set; }
        public int EPAGreenScore { get; set; }
        public int Axles { get; set; }
     }
}

第六步:创建一个 IVehicleRepository 接口

  
 
namespace VehicleStore.Models
{
    interface IVehicleRepository
    {
        IEnumerable<VehicleInfo> GetAll();
        IEnumerable<VehicleInfo> GetVehiclesByYear(int year);
        IEnumerable<VehicleInfo> GetVehiclesByMake(string make);
        VehicleInfo Get(Guid Id);
        void Remove(Guid Id);
        bool Update(VehicleInfo item);
        VehicleInfo Add(VehicleInfo item);
    }
}

第七步:创建一个 Vehicle 仓库的具体实现。

注意:有多种实现方式,为清晰起见,已进行了简化。实际生产实现可能会使用 SQL Server 等数据存储或其间的 Entity Framework 层。

  

namespace VehicleStore.Models
{
    public class VehicleRepository : IVehicleRepository
    {
        /// <summary>
        /// This is not the right approach for production quality code. 
        /// This is for demo purposes only.
        /// </summary>
        private List<VehicleInfo> vehicles = new List<VehicleInfo>();

        public VehicleRepository()
        {
            // This is used to populate initial data.
            // NOTE: This is not the right approach for production quality code.
            PopulateInitialData();
        }
        
        public IEnumerable<VehicleInfo> GetAll()
        {
            return vehicles;
        }

        public IEnumerable<VehicleInfo> GetVehiclesByYear(int year)
        {
            return vehicles.FindAll(p => p.Year == year);
        }

        public IEnumerable<VehicleInfo> GetVehiclesByMake(string make)
        {
            return vehicles.FindAll(p => p.Make == make);
        }

        public VehicleInfo Get(Guid Id)
        {
            return vehicles.Find(p => p.Id == Id);
        }

        public void Remove(Guid Id)
        {
            vehicles.RemoveAll(p => p.Id == Id);
        }

        public bool Update(VehicleInfo item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            int index = vehicles.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            
            // This form of update is to remove and add again.
            vehicles.RemoveAt(index);
            vehicles.Add(item);
            
            return true;
        }

        public VehicleInfo Add(VehicleInfo item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            // A new GUID is assigned every time a new item is added.
            item.Id = System.Guid.NewGuid();
            vehicles.Add(item);
            return item;
        }

        private void PopulateInitialData()
        {
            Add(new VehicleInfo { Make = "Infiniti", Model = "FX 35", Year = 2009, Trim = "Crossover" });
            Add(new VehicleInfo { Make = "Toyota", Model = "RAV44", Year = 2005, Trim = "Sport" });
            Add(new VehicleInfo { Make = "Audi", Model = "A5", Year = 2010, Trim = "Convertible" });
        }
    }
} 

第八步:控制器类 - VehiclesController

删除标准的 ValuesController 类,并创建新的 VehiclesController.cs

注意:VehiclesController 继承自 ApiController 类。

注意事项

  • Route 属性在 Web API 2.0 中使用。它定义了基于属性的路由。路由是 Web API 将 URI 匹配到操作的方式。通过 Route 属性可以明确定义复杂的 URI 路由。
  • Route 属性定义了实际的入站 URI。后面跟着 HTTP 动词属性(例如:[HttpGet])。
  • 路由约束 - 例如,此属性 [Route("vehicles/{Id:guid}", Name="VehicleInfoById")] 指定了 ID 为 Guid 的请求将检索具有该值的特定车辆。路由约束可以作为过滤器来正确路由请求。 
  • 请参阅路由约束列表
  

namespace VehicleStore.Controllers
{
    public class VehiclesController : ApiController
    {
        // This is NOT a correct implementation. This is for demonstration purposes only.
        // NOTE: NOT for PRODUCTION USE.
        static readonly IVehicleRepository repository = new VehicleRepository();

        [Route("vehicles")]
        [HttpGet]
        public IEnumerable GetAllVehicles()
        {
            return repository.GetAll();
        }

        /// 
        /// The route attribute with the data type of GUID
        /// 
        /// 
        /// 
        [Route("vehicles/{Id:guid}", Name="VehicleInfoById")]
        public VehicleInfo GetVehicleInfo(Guid Id)
        {
            VehicleInfo item = repository.Get(Id);

            if (item == null)
            {
                //NOTE: Uncomment the following line of code to use HttpError object.
                //var message = string.Format("Product with id = {0} not found", id);
                //HttpError err = new HttpError(message);
                //return Request.CreateResponse(HttpStatusCode.NotFound, err);

                var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
                {
                    Content = new StringContent(string.Format("No vehicle with ID = {0} exists", Id)),
                    ReasonPhrase = "The given VehicleID was not found."
                };
                throw new HttpResponseException(resp);
            }

            return item;
        }

        [Route("vehicles/{make:alpha}")]
        public IEnumerable GetVehiclesByMake(string make)
        {
            return repository.GetAll().Where(
                p => string.Equals(p.Make, make, StringComparison.OrdinalIgnoreCase));
        }

        [Route("vehicles")]
        [HttpPost]
        public HttpResponseMessage PostVehicle(VehicleInfo item)
        {
            item = repository.Add(item);
            var response = Request.CreateResponse(HttpStatusCode.Created, item);

            string uri = Url.Link("VehicleInfoById", new { id = item.Id });
            response.Headers.Location = new Uri(uri);
            return response;
        }

        [Route("vehicles")]
        [HttpPut]
        public void PutVehicle(Guid Id, VehicleInfo item)
        {
            item.Id = Id;
            if (!repository.Update(item))
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

        [Route("vehicles")]
        [HttpDelete]
        public void DeleteVehicle(Guid Id)
        {
            VehicleInfo item = repository.Get(Id);
            if (item == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            repository.Remove(Id);
        }
    }
}

第九步:构建解决方案。

第十步:将解决方案发布到外部文件夹。

右键单击 VehicleStore 项目,然后选择“发布...” 

在以下屏幕中,选择“文件夹”作为发布选项,并指定一个本地文件夹。

第十一步:本地部署:打开 IIS 管理器,为 Web API 2.0 项目创建一个新网站。这里使用端口 8080 而不是 80。请参见以下屏幕截图了解实际配置。 

请注意以下屏幕截图中的应用程序池 DefaultAppPool 设置。选择“本地系统”作为标识。 

第十二步:使用 SOAP UI 进行单元测试  

在 SOAP UI 中创建一个新的 REST 项目。请参见以下屏幕截图中的端点。

结论

在本文中,我们创建了一个模型、一个模型仓库,并将 Web API 2 的配置放入继承自 ApiController 的 VehiclesController 中。其余配置放在 Global.asax 和 webApiConfig.cs 文件中。

整个代码(Visual Studio 2013 解决方案)均可下载。请告诉我您的反馈,我将据此更新文章。

延伸阅读

这是我 ASP.NET 导师 Chander Dhall 的一篇文章。本文分为两部分,我强烈建议您阅读两部分。

RESTful API 最佳实践

© . All rights reserved.