本文演示ABP框架如何使用AngularJs,ASP.NET MVC,Web API 和EntityFramework构建基于N层架构的多语言SPA应用程序


演示程序截图如上所示.

内容摘要

介绍

在这篇文章, 我将基于以下框架演示如何开发单页面的(SPA) 应用程序 :

  • ASP.NET MVC 和 ASP.NET Web API  web站点的基础框架.
  • Angularjs  SPA 框架.
  • EntityFramework  ORM (Object-Relational Mapping) 框架
  • Castle Windsor – 依赖注入框架.
  • Twitter Bootstrap – 前端框架.
  • Log4Net 来记录日志, AutoMapper 实体对象映射.
  • 和 ASP.NET Boilerplate 作为应用程序模板框架.

ASP.NET Boilerplate [1] 是一个开源的应用程序框架,它包含了一些常用的组件让您能够快速简单的开发应用. 它集成一些常用的基础框架. 比如依赖注入领域驱动设计 和分层架构. 本应用演示ABP如何实现验证,异常处理,本地化响应式设计.

使用 boilerplate 模板创建程序

ASP.NET Boilerplate给我们提供了一个非常好的构建企业应用的模板,以便节约我们构建应用程序的时间。
在www.aspnetboilerplate.com/Templates目录,我们可以使用模板创建应用。

这里我选择 SPA(单页面程序)使用AngularJs EntityFramework框架. 然后输入项目名称SimpleTaskSystem.来创建和下载应用模版.下载的模版解决方案包含5个项目. Core 项目是领域 (业务) 层, Application 项目是应用层, WebApi 项目实现了 Web Api 控制器, Web 项目是展示层,最后EntityFramework 项目实现了EntityFramework框架.

Note: 如果您下载本文演示实例, 你会看到解决方案有7个项目. 我把NHibernate和Durandal都放到本演示中.如果你对NHibernate,Durandal不感兴趣的话,可以忽略它们.

创建实体对象

我将创建一个简单的应用程序来演示任务和分配任务的人. 所以我需要创建Task实体对象和Person实体对象.

Task实体对象简单的定义了一些描述:CreationTime和Task的状态. 它同样包含了Person(AssignedPerson)的关联引用:

public class Task : Entity<long>
{
[ForeignKey("AssignedPersonId")]
public virtual Person AssignedPerson{ get; set; }
public virtual int? AssignedPersonId { get; set; }
public virtual string Description { get; set; }
public virtual DateTime CreationTime { get; set; }
public virtual TaskState State { get; set; } public Task()
{
CreationTime = DateTime.Now;
State = TaskState.Active;
}
}

Person实体对象简单的定义下Name:

public class Person : Entity
{
public virtual string Name { get; set; }
}

ASP.NET Boilerplate 给 Entity 类定义了 Id 属性. 从Entity 类派生实体对象将继承Id属性. Task 类从 Entity<long>派生将包含 long 类型的ID. Person 类包含 int 类型的ID. 因为int是默认的主键类型, 这里我不需要特殊指定.

我在这个 Core 项目里添加实体对象因为实体对象是属于领域/业务层的.

创建 DbContext

众所周知, EntityFramework使用DbContext 类工作. 我们首先得定义它. ASP.NET Boilerplate创建了一个DbContext模板给我们. 我们只需要添加 IDbSets给 Task and Person. 完整的 DbContext 类如下:

public class SimpleTaskSystemDbContext : AbpDbContext
{
public virtual IDbSet<Task> Tasks { get; set; }
public virtual IDbSet<Person> People { get; set; }
public SimpleTaskSystemDbContext(): base("Default")
{ }
public SimpleTaskSystemDbContext(string nameOrConnectionString): base(nameOrConnectionString)
{ }
}

我们还需要在web.config添加默认的连接字符串. 如下:

<add name="Default" connectionString="Server=localhost; Database=SimpleTaskSystem; Trusted_Connection=True;" providerName="System.Data.SqlClient" />

创建数据库迁移

我们将使用EntityFramework的Code First模式来迁移和创建数据库. ASP.NET Boilerplate模板默认支持签约但需要我们添加如下的Configuration 类:

internalinternal sealed class Configuration : DbMigrationsConfiguration<SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
{
context.People.AddOrUpdate(
p => p.Name, new Person {Name = "Isaac Asimov"}, new Person {Name = "Thomas More"}, new Person {Name = "George Orwell"}, new Person {Name = "Douglas Adams"}
);
}
}

另一种方法, 是在初始化的是添加4个Person. 我将创建初始迁移.打开包管理控台程序并输入以下命令:

Add-Migration “InitialCreate” 命令创建 InitialCreate 类如下:

public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable( "dbo.StsPeople",
c => new {
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id); CreateTable( "dbo.StsTasks",
c => new {
Id = c.Long(nullable: false, identity: true),
AssignedPersonId = c.Int(),
Description = c.String(),
CreationTime = c.DateTime(nullable: false),
State = c.Byte(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.StsPeople", t => t.AssignedPersonId)
.Index(t => t.AssignedPersonId);
} public override void Down()
{
DropForeignKey("dbo.StsTasks", "AssignedPersonId", "dbo.StsPeople");
DropIndex("dbo.StsTasks", new[] { "AssignedPersonId" });
DropTable("dbo.StsTasks");
DropTable("dbo.StsPeople");
}
}

我们已经创建了数据库类, 但是还没创建那数据库. 下面来创建数据库,命令如下:

PM> Update-Database

这个命令帮我们创建好了数据库并填充了初始数据:

当我们修改实体类时, 我们可以通过 Add-Migration 命令很容易的创建迁移类。需要更新数据库的时候则可以通过 Update-Database 命令. 关于更多的数据库迁移, 可以查看 entity framework的官方文档.

定义库

在领域驱动设计中, repositories 用于实现特定的代码. ASP.NET Boilerplate 使用 IRepository 接口给每个实体自动的创建 repository . IRepository 定义了常用的方法如 select, insert, update, delete 等,更多如下:

我们还可以根据我们的需要来扩展 repository . 如果需要单独的实现接口的话,首先需要继承 repositories 接口. Task repository 接口如下:

public interface ITaskRepository : IRepository<Task, long>
{
List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state);
}

这继承了ASP.NET Boilerplate 的 IRepository 接口. ITaskRepository 默认定义了这些方法. 我们也可以添加自己的方法 GetAllWithPeople(…).

这里不需要再为Person创建 repository 了,因为默认的方法已经足够. ASP.NET Boilerplate 提供了通用的 repositories 而不需要创建 repository 类. 在’构建应用程序服务层’ 章节中的TaskAppService 类中将演示这些..

repository 接口被定义在Core 项目中因为它们是属于领域/业务层的.

实现库

我们需要实现上述的 ITaskRepository 接口. 我们在EntityFramework 项目实现 repositories. 因此,领域层完全独立于 EntityFramework.

当我们创建项目模板, ASP.NET Boilerplate 在项目中为 repositories 定义了一些基本类: SimpleTaskSystemRepositoryBase. 这是一个非常好的方式来添加基本类,因为我们可以为repositories稍后添加方法. 你可以看下面代码定义的这个类.定义TaskRepository 从它派生:

public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository
{
public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state)
{
//In repository methods, we do not deal with create/dispose DB connections, DbContexes and transactions. ABP handles it.
var query = GetAll();
//GetAll() returns IQueryable<T>, so we can query over it.
//var query = Context.Tasks.AsQueryable();
//Alternatively, we can directly use EF's DbContext object.
//var query = Table.AsQueryable();
//Another alternative: We can directly use 'Table' property instead of 'Context.Tasks', they are identical.
//Add some Where conditions...
if (assignedPersonId.HasValue)
{
query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value);
}
if (state.HasValue)
{
query = query.Where(task => task.State == state);
}
return query
.OrderByDescending(task => task.CreationTime)
.Include(task => task.AssignedPerson) //Include assigned person in a single query .ToList();
}
}

上述代码 TaskRepository 派生自 SimpleTaskSystemRepositoryBase 并实现了 ITaskRepository.

构建应用程序服务层

应用程序服务层被用于分离表示层和领域层并提供一些界面的样式方法. 在 Application 组件中定义应用程序服务. 首先定义 task 应用程序服务的接口:

public interface ITaskAppService : IApplicationService
{
GetTasksOutput GetTasks(GetTasksInput input);
void UpdateTask(UpdateTaskInput input);
void CreateTask(CreateTaskInput input);
}

ITaskAppService继承自IApplicationService. 因此ASP.NET Boilerplate自动的提供了一些类的特性(像依赖注入和验证).现在我们来实现ITaskAppService:

public class TaskAppService : ApplicationService, ITaskAppService
{
//These members set in constructor using constructor injection.
private readonly ITaskRepository _taskRepository;
private readonly IRepository<Person> _personRepository; /// <summary>
///In constructor, we can get needed classes/interfaces.
///They are sent here by dependency injection system automatically.
/// </summary>
public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
{
_taskRepository = taskRepository;
_personRepository = personRepository;
} public GetTasksOutput GetTasks(GetTasksInput input)
{
//Called specific GetAllWithPeople method of task repository.
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
//Used AutoMapper to automatically convert List<Task> to List<TaskDto>.
return new GetTasksOutput
{
Tasks = Mapper.Map<List<TaskDto>>(tasks)
};
} public void UpdateTask(UpdateTaskInput input)
{
//We can use Logger, it's defined in ApplicationService base class.
Logger.Info("Updating a task for input: " + input);
//Retrieving a task entity with given id using standard Get method of repositories.
var task = _taskRepository.Get(input.TaskId);
//Updating changed properties of the retrieved task entity.
if (input.State.HasValue)
{
task.State = input.State.Value;
} if (input.AssignedPersonId.HasValue)
{
task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value);
}
//We even do not call Update method of the repository.
//Because an application service method is a 'unit of work' scope as default.
//ABP automatically saves all changes when a 'unit of work' scope ends (without any exception). }
public void CreateTask(CreateTaskInput input)
{
//We can use Logger, it's defined in ApplicationService class.
Logger.Info("Creating a task for input: " + input);
//Creating a new Task entity with given input's properties
var task = new Task { Description = input.Description };
if (input.AssignedPersonId.HasValue)
{
task.AssignedPersonId = input.AssignedPersonId.Value;
}
//Saving entity with standard Insert method of repositories.
_taskRepository.Insert(task);
}
}

TaskAppService 使用仓储来操作数据库. 它过在构造函数中注入仓储. ASP.NET Boilerplate实现了依赖注入, 所以我们可以自由的使用构造函数注入和属性注入 (更多的依赖注入章节查看 ASP.NET Boilerplate 文档).

注意:我们使用PersonRepository来注入IRepository<Person>. ASP.NET Boilerplate会自动给我们的实体创建库. 如果默认的库接口够用的话,我们就不需要在重新定义实体库类型了.

应用服务层的方法使用了Data Transfer Objects (DTOs)协议. 这是一个非常好的方式,我同样建议大家这么做. 但是你也没必要非这么做不可,如果你能处理你的问题并传输到展示层。.

GetTasks方法中,我们使用GetAllWithPeople方法,它返回List<Task>类型, 但我可能需要返回一个List<TaskDto>给展现层. 这时候AutoMapper自动的帮助我们把TaskDto对象转换为Task对象.GetTasksInput和GetTasksOutput是特殊的DTOs被定义在GetTasks方法中.

UpdateTask方法中,我从数据库返回Task(使用IRepository的Get方法)并更新Task属性.注意我并没有调用reponsitory的Update方法. ASP.NET Boilerplate实现工作单元模式. 所以,在应用服务层的所有改变都是以工作单元形式,并最后自动保存到数据库中.

CreateTask方法中,使用IRepository的Insert方法创建新Task到数据库.

ASP.NET Boilerplate的ApplicationService类提供一些属性来简化开发应用服务.例如,它定义了Logger给日志. 你也可以自己实现,但要继承IApplicationService接口(注意:ITaskAppService继承自IApplicationService).

验证

ASP.NET Boilerplate在服务层方法输入的参数. CreateTask method getsCreateTaskInput as parameter:

public class CreateTaskInput
{
public int? AssignedPersonId { get; set; }
[Required]
public string Description { get; set; }
}

这里的Description标记是必须输入的意思. 这里你可以使用任何的Data Annotation属性. 如果你需要使用自定义属性, 你可以继承ICustomValidate 接口来实现UpdateTaskInput:

public class UpdateTaskInput : ICustomValidate
{
[Range(1, long.MaxValue)]
public long TaskId { get; set; }
public int? AssignedPersonId { get; set; }
public TaskState? State { get; set; }
public void AddValidationErrors(List<ValidationResult> results)
{
if (AssignedPersonId == null && State == null)
{
results.Add(new ValidationResult("Both of AssignedPersonId and State can not be null in order to update a Task!", new[] { "AssignedPersonId", "State" }));
}
} public override string ToString()
{
return string.Format("[UpdateTask > TaskId = {0}, AssignedPersonId = {1}, State = {2}]", TaskId, AssignedPersonId, State);
}
}

你可以替换AddValidationErrors方法里面的内容来自定义错误代码.

处理异常

注意我们没有做任何的异常处理. ASP.NET Boilerplate 自动的处理了异常, 日志和返回友好的错误信息给客户端. 客户端仅需处理错误信息并显示给用户.  异常处理文档查看.

构建Web API服务

我把应用服务层暴露给远程客户端,以便AngularJs能够简单的使用AJAX调用.

ASP.NET Boilerplate使用自动的方法来提供应该程序服务,即ASP.NET Web API.如DynamicApiControllerBuilder:

DynamicApiControllerBuilder
.ForAll<IApplicationService>(Assembly.GetAssembly(typeof (SimpleTaskSystemApplicationModule)), "tasksystem")
.Build();

在本段代码中, ASP.NET Boilerplate找所有继承自IApplicationService接口的方法并创建web api controller给每个应用程序服务类. 这里还有几种其他的查找control的方法.我们将在如何使用AJAX调用服务中看到.

开发SPA

我要在项目中实现一个单页面的web应用程序. AngularJs(Google开发的)是一个最流行的最火的SPA框架.

ASP.NET Boilerplate提供了一个模版来简单的使用AngularJs.这个模版有两个页面(Home 和About)能平滑过度. 使用了Twitter的Bootstrap前端框架.ASP.NET Boilerplate它默认定义了English和Turkish两种本地语言(你可以简单的添加删除语言).

我们先改变路由模版. ASP.NET Boilerplate模版使用AngularUI-Router, 这实际上是标准的AngularJs路由.它提供了路由状态模式. 我们有两个views: task list 和 new task. 所以我们将在app.js中来做如下的定义:

app.config([ '$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('tasklist', {
url: '/',
templateUrl: '/App/Main/views/task/list.cshtml',
menu: 'TaskList' //Matches to name of 'TaskList' menu in SimpleTaskSystemNavigationProvider })
.state('newtask', {
url: '/new',
templateUrl: '/App/Main/views/task/new.cshtml',
menu: 'NewTask' //Matches to name of 'NewTask' menu in SimpleTaskSystemNavigationProvider });
}
]);

app.js是javascript的入口文件,用来配置SPA的启动. 注意这里使用的是cshtml示图文件! 一般情况下, AngularJs的显示页面是html. 而ASP.NET Boilerplate使用cshtml文件. 因此强大的razor引擎将会把cshtml生成HTML.

ASP.NET Boilerplate提供了基础框架来创建和显示菜单.可以用c#或javascript语言来定义菜单. SimpleTaskSystemNavigationProvider类来创建菜单 ,header.js/header.cshtml用来显示菜单.

首先我们创建一个Angular controllertask列表页面:

(function() { var app = angular.module('app'); var controllerId = 'sts.views.task.list';
app.controller(controllerId, [ '$scope', 'abp.services.tasksystem.task',
function($scope, taskService) { var vm = this; vm.localize = abp.localization.getSource('SimpleTaskSystem'); vm.tasks = []; $scope.selectedTaskState = 0; $scope.$watch('selectedTaskState', function(value) {
vm.refreshTasks();
}); vm.refreshTasks = function() {
abp.ui.setBusy( //Set whole page busy until getTasks complete null,
taskService.getTasks({ //Call application service method directly from javascript state: $scope.selectedTaskState > 0 ? $scope.selectedTaskState : null }).success(function(data) {
vm.tasks = data.tasks;
})
);
}; vm.changeTaskState = function(task) { var newState; if (task.state == 1) {
newState = 2; //Completed } else {
newState = 1; //Active } taskService.updateTask({
taskId: task.id,
state: newState
}).success(function() {
task.state = newState;
abp.notify.info(vm.localize('TaskUpdatedMessage'));
});
}; vm.getTaskCountText = function() { return abp.utils.formatString(vm.localize('Xtasks'), vm.tasks.length);
};
}
]);
})();

我定义了名为’sts.views.task.list‘的控制器. 这是我的命名习惯(for scalable code-base)但你也可以简单的命名为’ListController’. AngularJs也使用依赖注入. 我们这里注入’$scope‘和’abp.services.tasksystem.task‘.首先是Angular的scope变量再次是ITaskAppService自动创建的的javascript服务代理(我们在’Build Web API services’之前就创建了).

ASP.NET Boilerplate 提供了基础设施本地语言文件用于服务端和客户端.

vm.taks是页面的任务列表.vm.refreshTasks方法内执行了taskService获取task的数据集合.这是在selectedTaskState修改的时候被调用(查看执行使用$scope.$watch).

正如你所看到的,调用应用服务的方法是如此的简单!这就是ASP.NET Boilerplate的特性.它生成了Web API层和Javascript代理层.因此我们调用应用服务层像调用javascript方法一样. 它完全集成进了AngularJs (使用Angular的$http service).

我们来看看任务列表的页面:

<div class="panel panel-default" ng-controller="sts.views.task.list as vm">
<div class="panel-heading" style="position: relative;">
<div class="row">
<!-- Title -->
<h3 class="panel-title col-xs-6"> @L("TaskList") - <span>{{vm.getTaskCountText()}}</span> </h3>
<!-- Task state combobox -->
<div class="col-xs-6 text-right">
<select ng-model="selectedTaskState">
<option value="0">@L("AllTasks")</option>
<option value="1">@L("ActiveTasks")</option>
<option value="2">@L("CompletedTasks")</option>
</select>
</div>
</div>
</div>
<!-- Task list -->
<ul class="list-group" ng-repeat="task in vm.tasks">
<div class="list-group-item">
<span class="task-state-icon glyphicon" ng-click="vm.changeTaskState(task)" ng-class="{'glyphicon-minus': task.state == 1, 'glyphicon-ok': task.state == 2}"></span>
<span ng-class="{'task-description-active': task.state == 1, 'task-description-completed': task.state == 2 }">{{task.description}}</span>
<br />
<span ng-show="task.assignedPersonId > 0"><span class="task-assignedto">{{task.assignedPersonName}}</span>
</span>
<span class="task-creationtime">{{task.creationTime}}</span>
</div>
</ul>
</div>

ng-controller 属性(在第一行) 绑定页面的controller. @L(“TaskList”) 获取”task list”的本地语言文本(服务器端解析html的时候执行). 因为这是个cshtml文件.

ng-model 绑定combobox和javascript变量. 当变量值改变combobox就会被更新.当改变combobox变量就会被更新. 这是AngularJs的双向绑定.

ng-repeat 是Angular的另一个指令用于循环集合里面的值. 当集合改变(例如增加值),它会自动更新界面. 这是AngularJs的另一个强大特性.

注意: 当你应该在页面添加javascript文件 (例如, 添加’task list’控制器)时.可以改成在添加Home\Index.cshtml模版时添加.

本地化

ASP.NET Boilerplate提供了灵活健壮的本地化系统.你可以使用XML文件或者资源文件做为本地化的数据源.你也可以自定义数据源.更多信息可以查看文档. 本示例使用XML文件演示(在web应用项目的Localization文件夹里):

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
<texts>
<text name="TaskSystem" value="Task System" />
<text name="TaskList" value="Task List" />
<text name="NewTask" value="New Task" />
<text name="Xtasks" value="{0} tasks" />
<text name="AllTasks" value="All tasks" />
<text name="ActiveTasks" value="Active tasks" />
<text name="CompletedTasks" value="Completed tasks" />
<text name="TaskDescription" value="Task description" />
<text name="EnterDescriptionHere" value="Task description" />
<text name="AssignTo" value="Assign to" />
<text name="SelectPerson" value="Select person" />
<text name="CreateTheTask" value="Create the task" />
<text name="TaskUpdatedMessage" value="Task has been successfully updated." />
<text name="TaskCreatedMessage" value="Task {0} has been created successfully." />
</texts>
</localizationDictionary>

使用单元测试

ASP.NET Boilerplate 是可测试的. 我的另一篇文章展示了如何使用ABP 基本项目集成集成单元测试. 查看文章: Unit testing in C# using xUnit, Entity Framework, Effort and ASP.NET Boilerplate.

摘要

在这篇文章, 我阐述了如何在 ASP.NET MVC web 应用中开发N层架构的SPA应用. ASP.NET Boilerplate 使用非常好的方式并且如此简单的创建了应用. 下面的链接可以获得更多信息:

文章历史

  • 2016-10-26: Upgraded sample project to ABP v1.0.
  • 2016-07-19: Updated article and sample project for ABP v0.10.
  • 2015-06-08: Updated article and sample project for ABP v0.6.3.1.
  • 2015-02-20: Added link to unit test article and updated the sample project
  • 2015-01-05: Updated sample project for ABP v0.5.
  • 2014-11-03: Updated article and sample project for ABP v0.4.1.
  • 2014-09-08: Updated article and sample project for ABP v0.3.2.
  • 2014-08-17: Updated sample project to ABP v0.3.1.2.
  • 2014-07-22: Updated sample project to ABP v0.3.0.1.
  • 2014-07-11: Added screenshot of ‘Enable-Migrations’ command.
  • 2014-07-08: Updated sample project and article.
  • 2014-07-01: First publish of the article.

引用

[1] ASP.NET Boilerplate 官网: http://www.aspnetboilerplate.com

[译]ABP框架使用AngularJs,ASP.NET MVC,Web API和EntityFramework构建N层架构的SPA应用程序的更多相关文章

  1. ABP 教程文档 1-1 手把手引进门之 AngularJs, ASP.NET MVC, Web API 和 EntityFramework(官方教程翻译版 版本3.2.5)含学习资料

    本文是ABP官方文档翻译版,翻译基于 3.2.5 版本 转载请注明出处:http://www.cnblogs.com/yabu007/  谢谢 官方文档分四部分 一. 教程文档 二.ABP 框架 三. ...

  2. ABP示例程序-使用AngularJs,ASP.NET MVC,Web API和EntityFramework创建N层的单页面Web应用

    本片文章翻译自ABP在CodeProject上的一个简单示例程序,网站上的程序是用ABP之前的版本创建的,模板创建界面及工程文档有所改变,本文基于最新的模板创建.通过这个简单的示例可以对ABP有个更深 ...

  3. ASP.NET MVC Web API For APP

    近来很多大型的平台都公开了Web API.比如百度地图 Web API,做过地图相关的人都熟悉.公开服务这种方式可以使它易于与各种各样的设备和客户端平台集成功能,以及通过在浏览器中使用 JavaScr ...

  4. 【转载】ASP.NET MVC Web API 学习笔记---联系人增删改查

    本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系 ...

  5. ASP.NET MVC Web API 学习笔记---第一个Web API程序

    http://www.cnblogs.com/qingyuan/archive/2012/10/12/2720824.html GetListAll /api/Contact GetListBySex ...

  6. 实战 ASP.NET MVC Web API

    实战 ASP.NET MVC Web API Web API 框架基于 ASP.NET MVC 框架开发,是一个面向 Http 协议的通信框架.相对于 WCF 而言,Web API 只面向于 Http ...

  7. ASP.NET MVC Web API Post FromBody(Web API 如何正确 Post)

    问题场景: ASP.NET MVC Web API 定义 Post 方法,HttpClient 使用 JsonConvert.SerializeObject 传参进行调用,比如 Web Api 中定义 ...

  8. Asp.net mvc web api 在项目中的实际应用

    Asp.net mvc web api 在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而web api在数据传输方面有多种实现方式,具体可根据实际情况而定! 1:数据传输前的加密,以下用到 ...

  9. asp.net mvc+web api+easyui

    前奏:第一次写博客,记录一下学习和开发的过程. 现在写的是一个后台管理系统,有基本的权限功能,其他功能都可以扩展.用到的技术是 asp.net mvc5,web api 2,entityframewo ...

随机推荐

  1. Android—Work—1day

    1.view 跟Activity 的数据传递通过 adapter 实现. http://blog.csdn.net/tianfeng701/article/details/75578192.Activ ...

  2. EA使用

    类逻辑图 关系1:泛化(继承),Driver和Northeastermer继承了Person类 关系2:实现,Northeastermer实现了LivingLeiFeng类 关系3:  关联,两个对象 ...

  3. C++ 实现Range类,用于常规遍历

    PYTHON的Range类非常好用,所以用C++来简单实现下:  // 实现Range类,用于遍历 // #include <string> class Range { public: / ...

  4. 修改.gitignore后让其生效

    在使用git的时候我们有时候需要忽略一些文件或者文件夹.我们一般在仓库的根目录创建.gitignore文件在提交之前,修改.gitignore文件,添加需要忽略的文件.然后再做add commit p ...

  5. sublimetext 3 set

    from https://segmentfault.com/a/1190000002596724{ "font_size": 21, "highlight_line&qu ...

  6. cpio命令用法

    [转自]流浪妖精のSKY    http://www.cnitblog.com/flutist1225/articles/18974.html cpio命令用法 cpio命令     利用cpio 可 ...

  7. oracle批量修改多个表的数据

    方法一 写PL/SQL,开cursor declare  l_varID varchar2(20);  l_varSubName varchar2(30);  cursor mycur is sele ...

  8. Redis安装手册

    转载请注明出处:http://www.cnblogs.com/robinjava77/p/5465146.html (Robin) 1)下载redis:wget http://download.red ...

  9. 移动端Viewport & 使用rem来开发移动端网站

    Viewport大神 无双 的精彩解释 具体参数各型号是否支持参见: http://www.cnblogs.com/2050/p/3877280.html#commentform 摘录: 移动设备上的 ...

  10. jquery高级函数

    .get() 将jq对象转成js $('#div1').get(0).innerHTML.text() 给标签添加文本 .detach() 和remove方法一样,但保留删除元素的所有行为$('div ...