Click here to Skip to main content
15,867,488 members
Articles / Web Development / XHTML

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

Rate me:
Please Sign up or sign in to vote.
3.31/5 (12 votes)
28 May 2017CPOL3 min read 42.3K   18   5
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)


Written By
Architect
Vietnam Vietnam
I have more than 8 years in web development for multiple types of applications (ERP, Education System, ...).
I usually organize training/ coaching on specified topic (such as: RESTful/ WebApi, Angular2, BEM, LESS, SASS, EF, NodeJs ....). Please contact me on Skype (tranthanhtu83) or email (contact@tranthanhtu.vn) if need.
For more information about me, Please visit http://www.tranthanhtu.vn/page/about-me

Comments and Discussions

 
QuestionSecurity - don't indicate which of username or password is incorrect Pin
Doug Domeny13-Jan-17 10:43
professionalDoug Domeny13-Jan-17 10:43 
AnswerRe: Security - don't indicate which of username or password is incorrect Pin
tranthanhtu.vn13-Jan-17 18:42
professionaltranthanhtu.vn13-Jan-17 18:42 
GeneralRe: Security - don't indicate which of username or password is incorrect Pin
Doug Domeny16-Jan-17 8:34
professionalDoug Domeny16-Jan-17 8:34 
Yes, after a closer reading I see the individual messages are for empty values, not a mismatch.
GeneralMy vote of 2 Pin
Arsenio Inojosa10-Nov-16 3:32
professionalArsenio Inojosa10-Nov-16 3:32 
AnswerRe: My vote of 2 Pin
tranthanhtu.vn10-Nov-16 3:42
professionaltranthanhtu.vn10-Nov-16 3:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.