## 6.6 ABP表现层 - AJAX API
### 6.6.2.1 AJAX操作问题
现代的应用经常会使用AJAX,尤其是单页应用,几乎是和服务器通信的唯一手段,执行AJAX通常会有以下步骤:
+ 基本上:为了执行一个AJAX调用,首先你要在客户端提供一个可供请求的URL,选取提交数据和一个方法(GET,POST,PUT,DELETE)。
+ 等待调用完成后,处理返回结果。当执行AJAX调用服务器端的时候,可能会有错误(一般是网络错误)。当然也有可能是服务器端产生了一些错误,对于这些错误会,服务器会返回一个失败的响应并且附上错误消息给客户端。
+ 客户端代码应该处理这些错误,并且可以选择通知用户(可以显示一个错误对话框)。如果没有错误且服务器端返回了数据,客户端必须处理它。还有你应该限制页面的某个区域(或者整个页面),并显示一个忙碌的指示直到AJAX操作完成。
+ 服务器端在得到请求后执行服务器端代码,捕获异常并返回一个有效的响应给客户端。在错误情况下,可以选择发送错误消息给客户端。如果是验证错误,服务器端可以添加验证错误的验证信息。在成功情况下,可以发送返回值给客户端。
### 6.6.2.2 ABP的方式
由于使用 **abp.ajax** 函数对AJAX调用进行了封装, 所以ABP能自动化这些步骤。下面是一个AJAX调用示例:
```javascript
var newPerson = {
name: 'Dougles Adams',
age: 42
};
abp.ajax({
url: '/People/SavePerson',
data: JSON.stringify(newPerson)
}).done(function(data) {
abp.notify.success('created new person with id = ' + data.personId);
});
```
abp.ajax得到 **options** 作为对象。你可以传递任何有效的jQuery的 [$.ajax](http://api.jquery.com/jQuery.ajax/) 函数中的参数。有一些默认参数:dataType 是 **json**,type是 **POST**,还有 contentType是 **application/json**(在发送数据到服务器端之前,我们需要调用 **JSON.stringify** 将脚本对象转换为JSON字符串)。通过对apb.ajax传递options可以覆盖默认值。
abp.ajax返回[promise](http://api.jquery.com/deferred.promise/)。因此,你可以写这些处理函数:done,fail,then等等。在这个例子中,我们对 **PeopleController's SavePerson action** 发送了一个简单的AJAX请求。在 **done** 处理函数中,我们对新创建的person取得了它的主键id并且显示了创建成功的通知。让我们看看 **MVC Controller**:
```csharp
public class PeopleController : AbpController
{
[HttpPost]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: 保存新创建的person到数据库并且返回person的id
return Json(new {PersonId = 42});
}
}
```
正如你猜测的 **SavePersonModel** 包含了Name和Age属性。**SavePerson** 被标记为 **HttpPost** 特性,因为abp.ajax默认方法是POST。通过返回了匿名对象简化了方法实现。
这个看上去很简单直白,但是ABP在背后做了很多重要的处理。让我们深入了解一下:
### 6.6.2.3 AJAX 返回消息
即使我们直接的返回了一个带有PersonId = 2 的对象,ABP也会使用 **MvcAjaxResponse** 对象来包装它。事实上AJAX响应返回的内容应该像下面一样:
```json
{
"success": true,
"result": {
"personId": 42
},
"error": null,
"targetUrl": null,
"unAuthorizedRequest": false,
"__abp": true
}
```
在这里所有的属性都是驼峰命名的(因为这在JavaScript中是惯例),即使在服务端代码中是PascalCased的。下面解释一下所有的字段:
+ **success**:boolean类型的值(true或者false),用来表示操作的成功状态。如果是ture,abp.ajax会解析该promise并且调用 **done** 函数。如果是false(在方法被调用的时候,如果有个异常被抛出),它会调用 **fail** 函数并且使用 abp.message.error 函数显示 **error** 消息。
+ **result**:控制器的action的实际返回值。如果success是ture并且服务器发送了返回值那么它才是有效的。
+ **error**:如果success是false,这个字段是一个包含 **message和details** 字段的对象。
+ **targetUrl**:如果需要的话,这提供了一种可能性:服务器端发送一个URL到客户端,使客户端可以重定向到其它的URL。
+ **unAuthorizedRequest**:这提供了一种可能性:服务器端发送通知给客户端该操作未被授权,或者是未认证用户。如果该值是true,那么abp.ajax会 **reloads** 当前的页面。
+ **__abp**:通过ABP包装响应返回的特殊签名。你自己不需要用到它,但是abp.ajax会处理它。
这种格式的对象会被 **abp.ajax** 函数识别且处理。abp.ajax会得到控制器的实际返回值(一个带有personid属性的对象),如果没有错误的话,那么你会在done函数中处理返回值。
### 6.6.2.4 处理错误
正如上面所述,ABP在服务器端处理异常,并且返回一个带有错误消息的对象。如下所示:
```json
{
"targetUrl": null,
"result": null,
"success": false,
"error": {
"message": "An internal error occured during your request!",
"details": "..."
},
"unAuthorizedRequest": false,
"__abp": true
}
```
正如你看到的,**success是false** 并且 **result是null**。abp.ajax处理这个对象,并且使用abp.message.error函数来显示错误消息给用户。如果你的服务器端代码抛出了 **UserFriendlyException** 类型的异常。它会直接的显示异常信息给用户。否则,它会隐藏实际的错误(将错误写入日志),并且显示一个标准的“服务器内部错误...”信息给用户。所有的这些都是ABP自动处理的。
你可能想为某个特别的AJAX调用禁止显示消息,那么添加 ** abpHandleError: false** 到 **abp.ajax的options**。
#### HTTP状态码
在异常发生的时候,ABP会返回给定的HTTP状态码:
+ **401**:未经身份验证的请求(没有登录,但是服务器端需要身份验证);
+ **403**:未授权的请求;
+ **500**:所有其它类型的异常。
### 6.6.2.5 WrapResult和DontWrapResult特性
使用 **WrapResult和DontWrapResult** 特性,可以对控制器的某个action或者所有的action来控制包装。
#### ASP.NET MVC 控制器
如果返回的类型是 **JsonResult(或者Task\<JsonResult\>)**,那么ABP会默认包装ASP.NET MVC action的返回结果。你可以使用 **WrapResult** 特性来改变它。如下所示:
```csharp
public class PeopleController : AbpController
{
[HttpPost]
[WrapResult(WrapOnSuccess = false, WrapOnError = false)]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: 保存新创建的person到数据库并且返回person的id
return Json(new {PersonId = 42});
}
}
```
作为一个快速开发方式,我们只能使用 **[DontWrapResult]** 特性在这个相同的示例上。
你可以在[启动配置](225824)里面改变这个默认的行为(使用 Configuration.Modules.AbpMvc()...)。
#### ASP.NET Web API 控制器
如果action被成功执行,ABP **不会默认包装** Web API Action的返回结果。如果需要的话,你可以添加WrapResult特性到action或者控制器上。但是它会 **包装异常**。
你可以在[启动配置](225824)里面改变这个默认的行为(使用 Configuration.Modules.AbpWebApi()...)。
#### 动态Web API层
**默认** ABP会 **包装** 动态Web API层的所有方法。你可以在你应用服务的接口上使用 **WrapResult和DontWrapResult** 特性来改变这个行为。
你可以在[启动配置](225824)里面改变这个默认的行为(使用 Configuration.Modules.AbpWebApi()...)。
#### ASP.NET Core 控制器
ABP会自动包装JsonResult,ObjectRes以及那些没有实现IActionResult对象。详情请查阅[ASP.NET Core文档](225864)。
你可以在[启动配置](225824)里面改变这个默认的行为(使用 using Configuration.Modules.AbpAspNetCore()...)。
### 6.6.2.6 动态Web API层
虽然ABP提供了一种调用Ajax的简单机制,但是在真实世界的应用中,为每个Ajax调用编写javascript函数是很经典的。例如:
```javascript
//创建一个抽象了Ajax调用的function
var savePerson = function(person) {
return abp.ajax({
url: '/People/SavePerson',
data: JSON.stringify(person)
});
};
//创建一个新的 person
var newPerson = {
name: 'Dougles Adams',
age: 42
};
//保存该person
savePerson(newPerson).done(function(data) {
abp.notify.success('created new person with id = ' + data.personId);
});
```
这是一个最佳实践,但是对每个AJAX调用函数都这样做,那是耗时且乏味的。对于[应用服务](225840)和控制器,ABP能够自动的生成这些函数。
详情请阅读[动态Web API层文档](225847)和[ASP.NET Core文档](225864)。
- 文章&教程
- Abp
- 1.1ABP总体介绍-入门介绍
- 1.2ABP总体介绍-多层架构体系
- 1.3ABP总体介绍-模块系统
- 1.4ABP总体介绍-启动配置
- 1.5ABP总体介绍-多租户
- 1.6ABP总体介绍-集成OWIN
- 2.1ABP公共结构-依赖注入
- 2.2ABP公共结构-会话管理
- 2.3ABP公共结构-缓存管理
- 2.4ABP公共结构-日志管理
- 2.5ABP公共结构-设置管理
- 2.6ABP公共结构-时区设置
- 3.1ABP领域层-实体
- 3.2ABP领域层-值对象
- 3.3ABP领域层-仓储
- 3.4ABP领域层-领域服务
- 3.5ABP领域层-工作单元
- 3.6ABP领域层-领域事件
- 3.7ABP领域层-数据过滤器
- 4.1ABP应用层-应用服务
- 4.2ABP应用层-数据传输对象
- 4.3ABP应用层-数据传输对象验证
- 4.4ABP应用层-权限认证
- 4.5ABP应用层-功能管理
- 4.6ABP应用层-审计日志
- 5.1ABP分布式服务-ASP.NET WebApi
- 5.2ABP分布式服务-动态WebApi层
- 5.3ABP分布式服务-集成OData
- 5.4ABP分布式服务-集成SwaggerUI
- 6.1ABP表现层-Mvc控制器
- 6.2ABP表现层-Mvc视图
- 6.3ABP表现层-本地化
- 6.4ABP表现层-导航栏
- 6.5ABP表现层-异常处理
- 6.6.2-AJAX
- 6.6.3-Notification
- 6.6.4-Message
- 6.6.5-UIBlockBusy
- 6.6.6-EventBus
- 6.6.7-Logging
- 6.6.8-OtherUtilities
- 6.6ABP表现层-JavascriptAPI
- 6.7ABP表现层-嵌入资源文件
- 6.8ASP.NET-Core
- 6.9CSRF和XSRF保护
- 7.1ABP后台服务-后台作业和工人
- 7.2ABP后台服务-集成Hangfire
- 8.1ABP实时服务-通知系统
- 8.2ABP实时服务-集成SignalR
- 9.1ABP基础设施层-集成Entity Framework
- 9.2ABP基础设施层-集成NHibernate
- 9.3ABP基础设施层-集成Entity Framework Core
- abp合并版160929
- md文档排版规范
- AbpZero
- 1.1ABPZero-概述
- 1.2ABPZero-安装
- 1.3ABPZero-启动模板
- 1.4ABPZero-启动模板Core
- 2.1ABPZero-多租户管理
- 2.2ABPZero-版本管理
- 2.4ABPZero-组织单位管理