TU Tran

Technologies should serve for business purpose.

NAVIGATION - SEARCH

[TinyERP]RESTful & WebApi

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 appreciate

For more information about other articles in this series

Thank you for reading,

Note: Please like and share to your friends if you think this is useful article, I really appreciate

 

 

Comments (1) -

This is the first time I frequented your web page and  to thispoint? I amazed with the research you made to create this actual post incredible.

Reply

Add comment