Category Archives: asp.net api

Angular 2 Tutorial – movies App

Last time I wrote a blog created the MovieAPI and now I am using that API as back end services to create an angular 2 app.

the final source code could find at my github repository.  here is the link download

For the MovieAPI open it, build it will install the packages automatically, then run the service first.

After the service is done, go to the angular2 folder, open command window

–run npm install,

— npm start

It will look like this:

home

create.PNG

added

Step 1 : Setting up your angular 2 application, I already have a blog  click here to found it. Or also could download from the source file from https://github.com/angular/quickstart/blob/master/README.md but it add a lot of unnecessary setting, probably confuse for beginning learner.

It was better do few times manually setting it up to get understand all of the necessary for start anguar 2 application.

Step 2:  In angualr 2 everything is component, for me normally like to create the service first. I just create a file named movies.service.ts under folder movies


import {Injectable} from 'angular2/core';
import {IMovie} from './movie';
import {Http, RequestOptions , Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';

@Injectable() 
export class MovieService {   
    private _movieUrl = 'http://localhost:53871/api/movies';

    constructor(private _http: Http) { }

    getMovies(): Observable<IMovie[]> {
        return this._http.get(this._movieUrl)
            .map((response: Response) => <IMovie[]>response.json()) //arrow function, tranceform response to json object and is array of IMovie[]
            .do(data => console.log("All: " + JSON.stringify(data))) //for debug
            .catch(this.handleError);
    }


    createMovie(movie: IMovie): Observable {
        let body = JSON.stringify(movie); 
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this._http.post(this._movieUrl, body, options)               
                .map(this.extractData)
                .catch(this.handleError);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body.data || {};
    }

    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

In there MovieService I have a IMovie here is my movie.ts I put it in the same folder with MovieService .


//interface won't compile to js file, it just for typescript do type checking
 export interface IMovie { 
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];

}


export class Movie implements IMovie {
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];
}

Step 3: Inject the services.
Go to the app.component.ts this is our container component, for anguar 2 only need inject once, then all of the nest container could using it ( by import it ).

Inject a service has two steps:

1) import the file into the app.component,

2) Inject it as providers

here is my app.component.ts

angular2_injection

Step 4: Create the movies-list.component.ts


//for using component, simply import it into this file
import {Component, OnInit} from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import {IMovie} from './movie';
import {MovieService} from './movies.service';
import {MovieFilterPipe} from './movie-filter.pipe';
import { StarComponent } from '../shared/star.component';

@Component({
    selector: 'sz-movies',
    templateUrl: 'app/movies/movies-list.component.html',
    styleUrls: ['app/movies/movies-list.component.css'],  
    pipes: [MovieFilterPipe],
    directives: [StarComponent, ROUTER_DIRECTIVES]
   
})
export class MovieListComponent implements OnInit {
    pageTitle: string = 'Movie List';   
   
    listFilter: string;
    errorMessage: string;
    movies: IMovie[];


    constructor(private _movieService: MovieService) {

    }
    
    ngOnInit(): void {
        this._movieService.getMovies()
            .subscribe(
            movies => this.movies = movies,
            error => this.errorMessage = error);
    }

}

here is the movies-list.component.htmlmovies_html

 

for this component I have a nested StarComponent.

Setp 5: create nested StarComponent, here is the star.component.ts under the share folder


import { Component, OnInit, Input} from 'angular2/core';
@Component({
    selector: 'sz-star',
    templateUrl: 'app/shared/star.component.html',
    styleUrls: ['app/shared/star.component.css']
})
export class StarComponent implements OnInit {
    @Input() rating: number;
    starWidth: number;
    ngOnInit(): void {
        this.starWidth = this.rating * 86 / 5;
    }
}

star.component.htmlstar.PNG

css:

star_css

In angular 2: data flow is unidirectional, for passing data from view to the class

first: you passing data into the directive could bind it to the property

passing_data_down.PNG

Second: need import the Input from angular2/core, and bind it to the property

inData

Now, one nested movies-list.component and nested star.componet down.

Now using the same steps create the MovieCreateComponent


import {Component, OnInit} from 'angular2/core';

import {IMovie, Movie} from '../movie';
import {Router} from 'angular2/router';
import {MovieService} from '../movies.service';

@Component({
    templateUrl: 'app/movies/create/movie-create.component.html'
})

export class MovieCreateComponent implements OnInit {
    title: string = 'Create New Movie';
    newMovie: IMovie;

    constructor(
        private _moviesService: MovieService,
        private _router: Router
    ) { }

    ngOnInit() {
        this.newMovie = new Movie;
    }

    createMovie(event) {
        let _this = this;

        this._moviesService.createMovie(this.newMovie)
            .subscribe(function () {
                _this._router.navigate(['Movies']);
            });
    }

    cancelCreate(event) {
        this._router.navigate(['Movies']);
    }

}angular2_form.PNG
Router:
route.PNG

In the index.html page, put the baseurl:
baseUrl.png

Done, have fun!

STEP BY STEP INSTRUCTION FOR BUILDING RESTFUL API WITH ASP.NET WEB API – PART II

Register the repository

when install the ninject packages, it will create a file NinjectWebCommon.cs  under the App_Start foler, open it, go the method: RegisterServices, register your service.


  private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind().To().InSingletonScope();
            kernel.Bind().To().InSingletonScope();
        }     

what is this do, you don’t need new a object, instead of the ninject container will create a instance in your controller or repositaory class. And the ninject also will handle the life cycle of the object.

Create  MovieDataViewModal.cs file under the ViewModal foler, we using it for validation and create and put Movie record.


  using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace MovieDemo.ViewModal
{
    public class MovieDataViewModal : IValidatableObject
    {
        public int MovieId { get; set; }
        [Required]
        [Remote("DoesTitleExist", "Movies")]
        public string Title { get; set; }
        [Required]
        public string Genre { get; set; }
        [Required]
        public string Classification { get; set; }
        [Range(0, 5)]
        public int Rating { get; set; }
        public int ReleaseDate { get; set; }
        public string[] Cast { get; set; }

        public IEnumerable Validate(ValidationContext validationContext)
        {
            var result = new List();

            if (Title == Genre)
            {
                result.Add(new ValidationResult("Title can not be the same as your Genre", new string[] { "Title" }));
            }
            return result;
        }
    }
}

In the Global.asax add the config for using Automap


Mapper.Initialize(config =>
            {
                config.CreateMap<MovieData, MovieDataViewModal>().ReverseMap();
            });

Create the controller

In the solution explorer, create MoviesController.cs undert the Controllers foler


using System;
using System.Web.Http;
using MoviesLibrary;
using System.Web.Http.OData;
using MovieDemo.Services;
using AutoMapper;
using MovieDemo.ViewModal;

namespace MovieDemo.Controllers
{
    public class MoviesController : ApiController
    {
        private readonly IDataRepository _repo;

        public MoviesController(IDataRepository dataRepository)
        {
            _repo = dataRepository;
        }

       
        [EnableQuery(PageSize = 5)]// this is for suport url query working with oData
        public IHttpActionResult Get()
        {
            try
            {
                var movies = _repo.GetAllMovies(); 
                return Ok(movies);
            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }

        }
    

        //GET: api/Movies/5
        public IHttpActionResult Get(int id)
        {
            try
            {
                if (id > 0)
                {
                    var movie = _repo.GetMovieById(id);
                    var result = Mapper.Map(movie);
                    if (result != null) return Ok(result);

                    return NotFound();
                }
                return NotFound();
            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }
        }

        // POST: api/Movies 
        public IHttpActionResult Post([FromBody]MovieDataViewModal newMovie) 
        {
            try
            {

                if (newMovie == null)
                    return BadRequest("Movie can not be null");

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                var movie = Mapper.Map(newMovie);

                if (_repo.CreateMovie(movie)) return Ok(newMovie);

                return Conflict();

            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }
        }

        // PUT : api/Movies/5
        public IHttpActionResult Put(int id, [FromBody]MovieDataViewModal movieModified)
        {
            try
            {

                if (movieModified == null)
                    return BadRequest("Movie can not be null");

                if (movieModified.MovieId <= 0 && movieModified.MovieId != id)
                    return Conflict();

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                var movieData = _repo.GetMovieById(id);
                if (movieData != null)
                {
                    var movie = Mapper.Map(movieModified);
                    _repo.UpdateMovie(movie); 
                    return Ok();
                }

                return NotFound();

            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }

        }

        public IHttpActionResult DoesTitleExist(string title)
        {
            if (_repo.DoesTitleAlreadyExist(title))
            {
                //Invalid
                return Ok();
            }
            //Valid
            return BadRequest("Title already existing");
        }
    }
}

then run the solution, open postman make query:

here is the example: http://localhost:53871/api/movies?$skip=2&$top=1
api06.PNG

for allmovies: api05.PNG

more detail  about odata query:
http://www.odata.org/documentation/odata-version-2-0/uri-conventions/

Step by step instruction for Building RESTful API with ASP.NET Web API – part I

vsUsing visio studio create a RESTful web API is very simple. In this tutorial I will follow the really live example and best practical to create a really useful web service.

source code download here:

https://github.com/sandywebdesigner/MovieWebApiDemo

It will included

  • using ninject register your service
  • using the most popularly repository pattern
  • oDATA to support url query
  • basic modal validation
  • AutoMapper
  • cache pattern

Create a new project

Start visual studio. From the File menu, select New > Project.

On the Templates, choose Web, then select ASP.NET Application, name it as MovieDemoapi01

In the pop out window, choose web api

api02

Install some  nuget packages I will using with this tutorial:

Ninject:

go to nuget search for ninject.MVC3 and install it,but this tutorial is for build web API, so also need install ninject webApi, see below is I installed for Ioc later using

api03

AutoMapper, search install the latest one.

OData

api04

For the data, I have one dll is called MovieLibrary, you could download it from my github. Included it into the project. here is the members I am going to use.


namespace MoviesLibrary
{
    public class MovieDataSource
    {
        public MovieDataSource();

        public int Create(MovieData movie);
        public List GetAllData();
        public MovieData GetDataById(int id);
        public void Update(MovieData movie);
    }
}

Now is the coding part start:
1)memory is cheaper,we put the retrive data into cache for fast query.
create a CacheHelper.cs ( I create a folder named helper holder this)


using System;
using System.Web;

namespace MovieDemo.Helper
{
    public static class CacheHelper
    {
        public static void SaveTocache(string cacheKey, object savedItem, DateTime absoluteExpiration)
        {
            if (IsIncache(cacheKey))
            {
                HttpContext.Current.Cache.Remove(cacheKey);
            }

            HttpContext.Current.Cache.Add(cacheKey, savedItem, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(24, 00, 0), System.Web.Caching.CacheItemPriority.Default, null);
        }

        public static T GetFromCache(string cacheKey) where T : class
        {
            return HttpContext.Current.Cache[cacheKey] as T;
        }

        public static void RemoveFromCache(string cacheKey)
        {
            HttpContext.Current.Cache.Remove(cacheKey);
        }

        public static bool IsIncache(string cacheKey)
        {
            return HttpContext.Current.Cache[cacheKey] != null;
        }
    }
}

2)create a folder named Services, create your repository, I create IDataRepository.cs and DataRepository.cs inside this folder.

for IDataRepository.cs:


using MoviesLibrary;
using System.Collections.Generic;
using System.Linq;

namespace MovieDemo.Services
{
    public interface IDataRepository
    {
        
        IQueryable GetAllMovies();
        MovieData GetMovieById(int id);
        bool CreateMovie(MovieData newMovie);
        bool UpdateMovie(MovieData movie);
        bool DoesTitleAlreadyExist(string title);
    }
}

3) the DataRepository.cs is the implementable of the IDataRepository, I put it along with the interface, so also under the Services folder.


using System;
using MoviesLibrary;
using System.Linq;
using MovieDemo.Common;
using MovieDemo.Helper;
using Ninject;

namespace MovieDemo.Services
{
    public class DataRepository : IDataRepository
    {   //here is the dependency inject. this need do extra in the ninject to     //regiester
        public DataRepository(MovieDataSource dataSrc)
        {
            this.dataSrc = dataSrc;
        }
       
        [Inject] private  MovieDataSource dataSrc { get; set; }
        public bool CreateMovie(MovieData newMovie)
        {
            try
            {
                if (dataSrc.Create(newMovie) > 0)
                {
                    CacheHelper.RemoveFromCache(MovieConstants.ALL_MOVIES);
                    return true;
                }
                return false;


            }
            catch(Exception ex)
            {
                return false;
            }
        }

       

        public IQueryable GetAllMovies()
        {

            var allMovies = CacheHelper.GetFromCache<IQueryable>(MovieConstants.ALL_MOVIES);

            if (allMovies == null)
            {

                allMovies = dataSrc.GetAllData().AsQueryable();
                CacheHelper.SaveTocache(MovieConstants.ALL_MOVIES, allMovies, DateTime.Now.AddHours(24));
            }

            return allMovies;
        }

        public MovieData GetMovieById(int id)
        {
            try
            {
                var allMovies = GetAllMovies();
                var movie = allMovies.FirstOrDefault(r => r.MovieId == id);
                if(movie == null)
                {
                    movie = dataSrc.GetDataById(id);
                    if (movie != null)
                        return movie;
                    return null;
                }
                return movie;
            }
            catch(Exception ex)
            {
                return null;
            }

        }

        public bool UpdateMovie(MovieData movie)
        {
            try
            {
                dataSrc.Update(movie);
                CacheHelper.RemoveFromCache(MovieConstants.ALL_MOVIES);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
            
        }

        public bool DoesTitleAlreadyExist(string title)
        {
           return GetAllMovies().Any(r => r.Title == title);
        }
    }
}

In above implement I using dependence inject, next post I will explain how to register this DataRepository, modal validation and create the MoviesController

— to be continue