使用Angular2及RESTful WebApi开发SPA类型的企业应用 - Part 6 RESTful 和 WebApi
作者:techcoaching,翻译:飘落寒冰
原文 有道
注:第一次翻译文章,有错译之处还请多多包涵,欢迎指出以便改进。
在本文中,我们将简单介绍本项目如何使用RESTful/WebApi
从Github上下载源码
该系列的全部文章
简介
本项目中,我们是用angular2(typescript)处理客户端逻辑和请求服务器端处理业务逻辑(C#)。
客户端和服务器通过WebApi(RESTful的Web Servic)来通信
客户端请求
在组件中,我们调用了相关的服务
@Component({
selector: "roles",
templateUrl: "app/modules/security/permission/permissions.html",
directives: [Grid, PageActions, Page]
})
export class Permissions extends BasePage {
private router: Router;
public model: PermissionsModel;
constructor(router: Router) {
super();
let self: Permissions = this;
self.router = router;
self.model = new PermissionsModel(self.i18nHelper);
self.loadPermissions();
this.model.addPageAction(new PageAction("btnAddPer", "security.permissions.addPermissionAction", () => self.onAddNewPermissionClicked()));
}
private loadPermissions() {
let self: Permissions = this;
permissionService.getPermissions().then(function (items: Array<any>) {
self.model.importPermissions(items);
});
}
}
permissionService实际上是通过IConnector(IConnect是angular中调用Http的接口)向服务器发送请求。
import configHelper from "../../../../common/helpers/configHelper";
import {Promise} from "../../../../common/models/promise";
import {IoCNames} from "../../../../common/enum";
import {IConnector} from "../../../../common/connectors/iconnector";
let permissionService = {
getPermissions: getPermissions
};
export default permissionService;
function getPermissions(): Promise {
let connector: IConnector = window.ioc.resolve(IoCNames.IConnector);
let url = String.format("{0}permissions", configHelper.getAppConfig().api.baseUrl);
return connector.get(url);
}
IConnector中,我们用了Http服务
export class RESTConnector implements IConnector {
private static http: Http;
private static eventManager: EventManager;
constructor() {
let http: Http = window.appState.getInjector().get(Http);
this.setHttp(http);
}
public get(url: string): Promise {
RESTConnector.eventManager.publish(LoadingIndicatorEvent.Show);
let def = PromiseFactory.create();
let headers = new JsonHeaders();
RESTConnector.http.get(url, { headers: headers })
.map((response: any) => response.json())
.subscribe(
(data: any) => this.handleResponse(def, data),
(exception: any) => this.handleException(def, exception)
);
return def;
}
}
服务器端API
我们通过PermissionsController接收请求
namespace App.Api.Features.Security
{
[RoutePrefix("api/permissions")]
public class PermissionsController : ApiController
{
[HttpGet]
[Route()]
public IResponseData<IList<PermissionAsKeyNamePair>> GetPermissions()
{
IResponseData<IList<PermissionAsKeyNamePair>> response = new ResponseData<IList<PermissionAsKeyNamePair>>();
try
{
IPermissionService permissionService = IoC.Container.Resolve<IPermissionService>();
IList<PermissionAsKeyNamePair> pers = permissionService.GetPermissions();
response.SetData(pers);
}
catch (ValidationException ex)
{
response.SetErrors(ex.Errors);
response.SetStatus(System.Net.HttpStatusCode.PreconditionFailed);
}
return response;
}
}
}
如果我们需要在请求时向server端传递参数,这些参数必须是DTO对。
RESTful规定了每种操作使用特定的Http Verb (Get, Create, Update, Delete) 和请求路径。我们不介绍这些内容。
我只想澄清一些不同之处:
我们定义了两种累行errors/excetion,分别用来返回:基础错误和业务错误
- Infrastructure Error
用来处理网络错误(找不到路径,超时,…), IIS配置错误configuration on IIS。这意味着业务逻辑部分尚未执行。
- Business Error
用来处理客户端传递到服务器端用来处理业务逻辑的数据有错误。
例如:用户的用户名和密码不匹配。这种情况下,我们不能用通过HttpCode 500传递,客户端不明白服务器端发生了什么事情。
所有我们需要把此错误返回给客户端
IResponseData<IList<PermissionAsKeyNamePair>> response = new ResponseData<IList<PermissionAsKeyNamePair>>();
try
{
}
catch (ValidationException ex)
{
response.SetErrors(ex.Errors);
response.SetStatus(System.Net.HttpStatusCode.PreconditionFailed);
}
return response;
REST工具的输出:
我们可以发现,API返回了多个校验错误,我们可以在UI中将他们展示出来。这种方式比返回一个通用错误要更友好。
在相应的service中,我们校验的了请求中的参数,如果校验失败,我们将抛出如下异常:
private void ValidateUserLoginRequest(UserSignInRequest request)
{
ValidationException exception = new ValidationException();
if (request == null)
{
exception.Add(new ValidationError("common.invalidRequest"));
}
if (String.IsNullOrWhiteSpace(request.Email))
{
exception.Add(new ValidationError("registration.signin.validation.emailRequired"));
}
if (String.IsNullOrWhiteSpace(request.Pwd))
{
exception.Add(new ValidationError("registration.signin.validation.pwdRequired"));
}
IUserRepository userRepository = IoC.Container.Resolve<IUserRepository>();
User userProfile = userRepository.GetByEmail(request.Email);
if (userProfile == null || EncodeHelper.EncodePassword(request.Pwd) != userProfile.Password)
{
exception.Add(new ValidationError("registration.signin.validation.invalidEmailOrPwd"));
}
exception.ThrowIfError();
}
总结
本例中,API成功返回了处理结果(HttpCode 200),但是收到的确是业务逻辑错误。
一些朋友认为这可能令人困惑。如果您有更好的解决方案,我很高兴与各位一起探讨。
授权
本文,以及所包含的源码,文件遵循CPOL协议