Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / XHTML

Angular2 & WebApi (SPA) for Enterprise App - Part 6 - RESTful & WebApi

3.31/5 (12 votes)
28 May 2017CPOL3 min read 43.2K  
In this article, We will have a look to unserstand how RESTful/WebApi was applied in my code

Other Articles in the Series

  1. Overview
  2. Add new Permission
  3. Project structure
  4. Multi-Languages (i18n)
  5. DI & IoC - Why and Why not?
  6. RESTful & WebApi
  7. Manage Application Lifecycle
  8. Build & Deploy Application
  9. New version of TinyERP with Angular 2 (typescript)
  10. CQRS: Avoiding performance issues in enterprise app (basic)
  11. Multiple data-stores: Scale your repository (Part 1)
  12. Multiple data-stores: Scale your repository (Part 2)

Introduction

In my code, we use angular2 (typescript) handles client logic and performs business logic on server side (c#).

The client communicates with server side through RESTful web service written in WepApi.

How to Get the Code

Please check out the code at https://github.com/techcoaching/TinyERP.

Calling from Client

In the page, we call to appropriated service:

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

In permissionService, it was actually use the IConnector (IConnector in order using Http from angular) and send request to server side.

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

In IConnector, we use Http service:

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

}

Handling on API (Server side)

We have the controller, we receive the request:

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

If we have parameters sent to server along with request. they should be mapped to DTO (Data Transfer Object).

In RESTful, we know which Http Verb should be used for specified action (Get, Create, Update, Delete) and appropriated URI. So I think we will not discuss here again.

There are some different, I just want to clarify:

There are 2 types of errors/ exception, we returned to client: Infrastructure Error and Business Error

Infrastructure Error

we use this for some errors related to network (not found, time out, ....), configuration on IIS. it means our business code was not executed.

Business Error

we use this for the case, data was sent from clien for doing specified business logic is invalid.

Such as: user name or pwd does not match. For this case, we should not return 500 HttpCode, as client may not understand what was happended on server side.

So we need to return the client side the code for that error.

IResponseData<IList<PermissionAsKeyNamePair>> response = new ResponseData<IList<PermissionAsKeyNamePair>>();
try
{
    
}
catch (ValidationException ex)
{
    response.SetErrors(ex.Errors);
    response.SetStatus(System.Net.HttpStatusCode.PreconditionFailed);
}
return response;

The output from REST client as below:

We can see, on the API can send multiple invalid validation back to client.

On the client we can show them on the UI. rather than common error message as photo below:

In the appropriated service, we validate the request and throw exception if invalid like this:

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

 

Summary

 

In some case, the response from the API success (status code 200) but we have the business logic exception.

Some of my friends told me that this may a little confuse.

If you have other better solution, Please let me know. I appriciate

For more information about:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)