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

Angular2 & WebApi (SPA) 用于企业应用 - 第 6 部分 - RESTful & WebApi

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.31/5 (12投票s)

2016 年 11 月 7 日

CPOL

3分钟阅读

viewsIcon

43760

在本文中,我们将深入了解如何在我的代码中应用 RESTful/WebApi。

系列中的其他文章

  1. 概述
  2. 添加新权限
  3. 项目结构
  4. 多语言 (i18n)
  5. DI & IoC - 为什么以及为什么不?
  6. RESTful & WebApi
  7. 管理应用生命周期
  8. 构建和部署应用
  9. TinyERP新版本(使用Angular 2 (typescript))
  10. CQRS:避免企业应用程序中的性能问题(基础)
  11. 多个数据存储:扩展你的存储库(第1部分)
  12. 多个数据存储:扩展你的存储库(第2部分)

引言

在我的代码中,我们使用 angular2 (typescript) 来处理客户端逻辑,并在服务器端 (c#) 执行业务逻辑。

客户端通过用 WepApi 编写的 RESTful Web 服务与服务器端进行通信。

如何获取代码

请查看代码:https://github.com/techcoaching/TinyERP

从客户端调用

在页面上,我们调用相应服务。

@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 (IConnector 是用于 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 (服务器端) 处理

我们有控制器,它接收请求。

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;
        }
    }
}

如果请求附带参数发送到服务器,它们应该被映射到 DTO (Data Transfer Object)。

在 RESTful 中,我们知道应该为指定的动作 (Get, Create, Update, Delete) 和相应的 URI 使用哪个 Http Verb。所以我想我们这里不再讨论。

有一些不同之处,我只想澄清一下。

我们返回给客户端的错误/异常有两种:基础设施错误和业务错误。

基础设施错误

我们将其用于与网络 (未找到、超时等)、IIS 配置相关的某些错误。这意味着我们的业务代码没有被执行。

业务错误

我们将其用于客户端发送的数据用于执行特定的业务逻辑无效的情况。

例如:用户名或密码不匹配。在这种情况下,我们不应返回 500 HttpCode,因为客户端可能不明白服务器端发生了什么。

所以我们需要向客户端返回该错误的编码。

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 上,而不是像下面这张照片那样的通用错误消息。

在相应服务中,我们像这样验证请求并在无效时抛出异常。

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 的响应是成功的 (状态码 200),但我们有业务逻辑异常。

我的一些朋友告诉我,这可能会有点令人困惑。

如果您有其他更好的解决方案,请告诉我。我将不胜感激。

有关更多信息

© . All rights reserved.