使用Angular2及WebApi开发SPA类型的企业应用 - Part 6- RESTful 和 WebApi

使用Angular2及RESTful WebApi开发SPA类型的企业应用 - Part 6 RESTful 和 WebApi
作者:techcoaching,翻译:飘落寒冰
原文 有道
注:第一次翻译文章,有错译之处还请多多包涵,欢迎指出以便改进。

在本文中,我们将简单介绍本项目如何使用RESTful/WebApi
从Github上下载源码

该系列的全部文章

  1. 概览
  2. 添加新角色
  3. 项目结构
  4. 多语言
  5. 依赖注入和控制反转-为什么和为什么不?
  6. RESTful & WebApi
  7. 应用生命周期管理
  8. 应用构建与部署

简介

本项目中,我们是用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工具的输出:
image

我们可以发现,API返回了多个校验错误,我们可以在UI中将他们展示出来。这种方式比返回一个通用错误要更友好。
image

在相应的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协议

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值