Search This Blog

Showing posts with label MVC. Show all posts
Showing posts with label MVC. Show all posts

Sunday, July 23, 2017

ASP.NET MVC với AngularJS 2. Bài 1: Cài đặt và cấu hình AngularJS 2 trong Visual Studio 2015 Community.

1. Tạo mới project ASP.NET MVC và AngularJS 2.

Mở Visual Studio, ở đây Kiên đang sử dụng Visual Studio 2015 Community và tiến hành tạo mới project ASP.NET Web Application (.NET Framework).

Đặt tên cho project là AngularJs4.

Trong phần Select a template, chọn MVC. Sau đó click OK.

Chờ một lúc để Visual Studio khởi tạo project. Hình bên dưới là cấu trúc project ASP.NET MVC với AngularJS 2 đã được tạo ra.

2. Setup môi trường cho AngularJS 2.
- Thêm package.json (npm Configuration file). File này sử dụng npm (NodeJS) để load các packages cần thiết dùng cho AngularsJS 2.
Trong trường hợp các bạn chưa cài đặt NodeJS, vào https://nodejs.org/ để cài đặt phiên bản mới nhất.
Ngoài ra để tìm hiểu thêm về npm, đọc ở đây.

- Thêm tsconfig.json (TypeScript configuration file). File này dùng để cấu hình trong việc biên dịch các file TypeScript (ts) sang Javascript (js) để chúng có thể chạy được trên các browser.
Bạn cũng cần phải cài đặt phiên bản mới nhất của TypeScript Ở ĐÂY, hoặc nếu bạn đã cài đặt NodeJS thì có thể dùng npm và gõ lệnh:
npm install -g TypeScript.

- Thêm systemjs.config.js. File này dùng để cấu hình client-side load các packages lên browser.

Để cấu hình nhanh và chính xác, cách tốt nhất là vào trang AngularJS 2 Tutorial, download phần example và tiến hành copy các files đã nói ở trên vào project của chúng ta.
Hình bên dưới là cấu trúc thư mục của phần example đã download và giải nén.

Copy các files:
package.json
tslink.json
bsconfig.json
src/tsconfig.json
src/systemjs.config.js
src/systemjs-angular-loader.js
vào project ASP.NET MVC của chúng ta.

Trong file package.json, tìm và xóa các đoạn text "src" vì thư mục gốc không phải là src như trong phần example. Nội dụng file sau khi cập nhật như bên dưới:
 {  
  "name": "angular-io-example",  
  "version": "1.0.0",  
  "private": true,  
  "description": "Example project from an angular.io guide.",  
  "scripts": {  
   "test:once": "karma start karma.conf.js --single-run",  
   "build": "tsc -p /",  
   "serve": "lite-server -c=bs-config.json",  
   "prestart": "npm run build",  
   "start": "concurrently \"npm run build:watch\" \"npm run serve\"",  
   "pretest": "npm run build",  
   "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",  
   "pretest:once": "npm run build",  
   "build:watch": "tsc -p / -w",  
   "build:upgrade": "tsc",  
   "serve:upgrade": "http-server",  
   "build:aot": "ngc -p tsconfig-aot.json && rollup -c rollup-config.js",  
   "serve:aot": "lite-server -c bs-config.aot.json",  
   "build:babel": "babel -d --extensions \".es6\" --source-maps",  
   "copy-dist-files": "node ./copy-dist-files.js",  
   "i18n": "ng-xi18n",  
   "lint": "tslint .//**/*.ts -t verbose"  
  },  
  "keywords": [],  
  "author": "",  
  "license": "MIT",  
  "dependencies": {  
   "@angular/animations": "~4.2.0",  
   "@angular/common": "~4.2.0",  
   "@angular/compiler": "~4.2.0",  
   "@angular/compiler-cli": "~4.2.0",  
   "@angular/core": "~4.2.0",  
   "@angular/forms": "~4.2.0",  
   "@angular/http": "~4.2.0",  
   "@angular/platform-browser": "~4.2.0",  
   "@angular/platform-browser-dynamic": "~4.2.0",  
   "@angular/platform-server": "~4.2.0",  
   "@angular/router": "~4.2.0",  
   "@angular/tsc-wrapped": "~4.2.0",  
   "@angular/upgrade": "~4.2.0",  
   "angular-in-memory-web-api": "~0.3.2",  
   "core-js": "^2.4.1",  
   "rxjs": "^5.1.0",  
   "systemjs": "0.19.39",  
   "zone.js": "^0.8.4"  
  },  
  "devDependencies": {  
   "@types/angular": "^1.5.16",  
   "@types/angular-animate": "^1.5.5",  
   "@types/angular-cookies": "^1.4.2",  
   "@types/angular-mocks": "^1.5.5",  
   "@types/angular-resource": "^1.5.6",  
   "@types/angular-route": "^1.3.2",  
   "@types/angular-sanitize": "^1.3.3",  
   "@types/jasmine": "2.5.36",  
   "@types/node": "^6.0.45",  
   "babel-cli": "^6.16.0",  
   "babel-preset-angular2": "^0.0.2",  
   "babel-preset-es2015": "^6.16.0",  
   "canonical-path": "0.0.2",  
   "concurrently": "^3.0.0",  
   "http-server": "^0.9.0",  
   "jasmine": "~2.4.1",  
   "jasmine-core": "~2.4.1",  
   "karma": "^1.3.0",  
   "karma-chrome-launcher": "^2.0.0",  
   "karma-cli": "^1.0.1",  
   "karma-jasmine": "^1.0.2",  
   "karma-jasmine-html-reporter": "^0.2.2",  
   "karma-phantomjs-launcher": "^1.0.2",  
   "lite-server": "^2.2.2",  
   "lodash": "^4.16.2",  
   "phantomjs-prebuilt": "^2.1.7",  
   "protractor": "~5.1.0",  
   "rollup": "^0.41.6",  
   "rollup-plugin-commonjs": "^8.0.2",  
   "rollup-plugin-node-resolve": "2.0.0",  
   "rollup-plugin-uglify": "^1.0.1",  
   "source-map-explorer": "^1.3.2",  
   "tslint": "^3.15.1",  
   "typescript": "~2.3.2"  
  },  
  "repository": {}  
 }  

Tương tự trong file bsconfig.json, tìm và xóa đoạn text "src".
 {  
  "server": {  
   "baseDir": "",  
   "routes": {  
    "/node_modules": "node_modules"  
   }  
  }  
 }  

File tsconfig.json.
 {  
  "compilerOptions": {  
   "target": "es5",  
   "module": "commonjs",  
   "moduleResolution": "node",  
   "sourceMap": true,  
   "emitDecoratorMetadata": true,  
   "experimentalDecorators": true,  
   "lib": [ "es2015", "dom" ],  
   "noImplicitAny": true,  
   "suppressImplicitAnyIndexErrors": true,  
   "typeRoots": [  
    "../node_modules/@types/"  
   ]  
  },  
  "compileOnSave": true,  
  "exclude": [  
   "node_modules/*",  
   "**/*-aot.ts"  
  ]  
 }  

File systemjs.config.js.
 /**  
  * System configuration for Angular samples  
  * Adjust as necessary for your application needs.  
  */  
 (function (global) {  
  System.config({  
   paths: {  
    // paths serve as alias  
    'npm:': 'node_modules/'  
   },  
   // map tells the System loader where to look for things  
   map: {  
    // our app is within the app folder  
    'app': 'app',  
    // angular bundles  
    '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',  
    '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',  
    '@angular/core': 'npm:@angular/core/bundles/core.umd.js',  
    '@angular/common': 'npm:@angular/common/bundles/common.umd.js',  
    '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',  
    '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',  
    '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',  
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',  
    '@angular/http': 'npm:@angular/http/bundles/http.umd.js',  
    '@angular/router': 'npm:@angular/router/bundles/router.umd.js',  
    '@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',  
    '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',  
    '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',  
    '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',  
    // other libraries  
    'rxjs':           'npm:rxjs',  
    'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'  
   },  
   // packages tells the System loader how to load when no filename and/or no extension  
   packages: {  
    app: {  
     main: './main.js',  
     defaultExtension: 'js',  
     meta: {  
      './*.js': {  
       loader: 'systemjs-angular-loader.js'  
      }  
     }  
    },  
    rxjs: {  
     defaultExtension: 'js'  
    }  
   }  
  });  
 })(this);  

Click phải lên file package.json, chọn Restore Packages để tiến hành cài đặt các packages của AngularJS 2 dùng cho project ASP.NET MVC của chúng ta.



Chờ trong giây lát, khi quá trình cài đặt package hoàn tất, trong Solution Explorer, click Show All Files, chúng ta sẽ thấy có một thư mục node_modules xuất hiện như hình bên dưới. Chú ý là chúng ta không include thư mục node_modules này vào project ASP.NET MVC.


3. Thêm các file TypeScript (ts) để bootstrap Angular 2 vào ASP.NET MVC.
Trong project của chúng ta, tạo mới folder và đặt tên là app như trong file systemjs.config.js đã khai báo.
   map: {  
    // our app is within the app folder  
    'app': 'app', 
Bên trong folder app, tiến hành thêm các files TypeScript (ts):
- app.module.ts: dùng để định nghĩa ra các module.
 import { NgModule } from '@angular/core';  
 import { BrowserModule } from '@angular/platform-browser';  
 import { AppComponent } from './app.component';  
 @NgModule({  
   imports: [BrowserModule],  
   declarations: [AppComponent],  
   bootstrap: [AppComponent]  
 })  
 export class AppModule { }  
- app.component.ts: dùng để định nghĩa các component dùng cho module.
 import { Component } from '@angular/core';  
 @Component({  
   selector: 'app',  
   template: '<h1>Page Says: {{text}}</h1>'  
 })  
 export class AppComponent { text = 'Hello World'; }  
Sau đó trong thư mục gốc, tạo file main.ts. Đây là đoạn bootstrap code mà AngularJS 2 dùng để load các module.
 import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';  
 import { AppModule } from './app/app.module';  
 platformBrowserDynamic().bootstrapModule(AppModule);  
Tiến hành build project, lúc này project sẽ xuất hiện các file .js và .js.map như hình bên dưới:


4. Cập nhật các file cshtml để load và render Angular 2.
Mở file Views\Shared\_Layout.cshtml, trong tag <head></head> bổ sung các scripts như đoạn code bên dưới.
 <head>  
   <meta charset="utf-8" />  
   <meta name="viewport" content="width=device-width, initial-scale=1.0">  
   <title>@ViewBag.Title - My ASP.NET Application</title>  
   @Styles.Render("~/Content/css")  
   @Scripts.Render("~/bundles/modernizr")  

   <script src="~/node_modules/core-js/client/shim.min.js"></script>  
   <script src="~/node_modules/zone.js/dist/zone.js"></script>  
   <script src="~/node_modules/systemjs/dist/system.src.js"></script>  
   <script src="systemjs.config.js"></script>  
   <script>  
     System.import('main.js').catch(function(err){ console.error(err); });  
   </script>  

 </head>  

Mở file Views\Home\Index.cshtml, cập nhật lại như đoạn code bên dưới:
 @{  
   ViewBag.Title = "Home Page";  
 }  
 <app>loading...</app>

Tag <app></app>  đã được định nghĩa trong selector: 'app' trong file app.component.ts.
 import { Component } from '@angular/core';  
 @Component({  
   selector: 'app',  
   template: '<h1>Page Says: {{text}}</h1>'  
 })  
 export class AppComponent { text = 'Hello World'; }  

Cuối cùng tiến hành build và chạy thử (F5).

Source code tham khào ở đây


--------
Share to be shared
--------

Sunday, July 3, 2016

Tạo SEO URL thân thiện người dùng trong ASP.NET MVC

Có nhiều cách SEO để làm cho website của bạn nổi bật, dễ tìm kiếm, xếp hạng cao trong các bộ máy tìm kiếm như Google hay Bing. Một trong những cách quan trọng nhất trong SEO đó là tạo ra một URL thân thiện người dùng và chứa những thông tin có ích, dễ hiểu. Điều này đặc biệt quan trọng vì các bộ máy tìm kiếm như Google hay Bing sẽ ưu tiên và xếp hạng cao những website có URL như https:\\www.yourwebsite\thu-thuat-mvc\tao-seo-url-than-thien-nguoi-dung-trong-aspnet-mvc hơn là URL như https:\\www.yourwebsite\thuthuatmvc\taoseourlthanthiennguoidungtrongaspnetmvc.

Trong bài viết này Kiên sẽ giới thiệu đến các bạn một cách dễ dàng nhất để tạo ra một SEO URL thân thiện người dùng (user friendly) thông qua ASP.NET MVC qua một ví dụ demo đơn giản.

Đầu tiên chúng ta hãy tạo một project có tên SEOUrl

Chúng ta chọn Project Template là MVC và nhấn OK.

Và đây là toàn bộ project của chúng ta

Hãy chọn Start Debugging (F5) để chạy thử website của chúng ta

Kiên giả sử chúng ta sẽ route lại URL của toàn bộ các trang Account như Login, Register (Kiên sử dụng các trang Account để triển khai nhanh demo của chúng ta, các bạn hoàn toàn có thể tạo ra các trang khác tùy theo mục đích của mình).

Giả sử như Kiên có yêu cầu là muốn thay đổi các URL mặc định là:
localhost\Account\Register
localhost\Account\Login 

thành những URL thân thiện hơn như:
localhost\tai-khoan\dang-ky
localhost\tai-khoan\dang-nhap. 


Với yêu cầu này, điều chúng ta cần quan tâm trong ASP.NET MVC là chúng ta phải rewrite lại tên của Controller và Action cũng như là các Parameter.

Trong ASP.NET MVC có tính năng rất hay để route, SEO URL của website gọi là URL Routing. Các bạn vào class RouteConfig nằm trong App_Start và thêm mới một route mà chúng ta muốn SEO URL. Chú ý là route nằm trên sẽ được ưu tiên hơn route bên dưới.

Đây là route mới mà Kiên tạo ra theo như yêu cầu bên trên.
       //create new route  
       routes.MapRoute(  
         name: "Account",  
         url: "tai-khoan/{action}/{id}",  
         defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional }  
       );  

Route mới của chúng ta có các giá trị:
- name: là tên của route chúng ta, chú ý đây là giá trị định danh và không được trùng.
- url: là URL pattern của route. Giá trị mặc định là "{controller}/{action}/{id}" và chúng ta hoàn toàn có thể custom lại. Ở đây Kiên đang tạo ra một route có URL là "tai-khoan/{action}/{id}". Các bạn chú ý là "tai-khoan" ở đây Kiên không khai báo dấu "{...}", điều này có nghĩa là nó sẽ được fix cứng trên và không thay đổi được trong URL, mục đích là sử dụng riêng cho các trang Account. Còn {action}/{id} được khai báo có dấu "{...}" đồng nghĩa action và id sẽ được thay đổi tùy theo giá trị đưa vào.
- defaults: là các giá trị mặc định của route bao gồm Controller, Action, ID tương ứng mà chúng ta khai báo trong url. Route của chúng ta sẽ mặc định sử dụng cho controller Account và các Action tương ứng của nó.

Sau khi thêm route mới, hãy Start Debugging (F5) để xem lại URL của các trang trong Account.

Trang Register: localhost\tai-khoan\Register

Trang Login: localhost\tai-khoan\Login


Các bạn sẽ thấy nó đã được route lại controller Account. Chúng ta cũng kiểm tra lại các trang khác ngoài Account để đảm bảo không bị ảnh hưởng sau khi chúng ta route Account.



Rất dễ dàng phải không! Nhưng khoan, chúng ta còn phải làm gì nữa. Hãy xem lại yêu cầu nào.
localhost\tai-khoan\dang-ky
localhost\tai-khoan\dang-nhap. 

Chúng ta mới chỉ route lại controller Account thành tai-khoan. Còn các action thì sao? Trong ASP.NET MVC chúng ta không thể tạo ra trực tiếp một action có tên là dang-ky hay dang-nhap. Thật may là ASP.NET MVC cho chúng ta giải pháp thay thế là thông qua việc sử dụng attribute [ActionName]. Attribute [ActionName] sẽ thay tên đổi họ cho action method của nó trong toàn hệ thống.

Hãy cập nhật lại action Register như sau:
- Thêm attribute [ActionName("dang-ky")] trước dòng khai báo public ActionResult Register().
- Thay đổi return View() thành return View("Register") để trả về view mà chúng ta mong muốn. Nếu bạn không làm bước xác định cụ thể view, mỗi khi bạn gọi đến action này thì nó sẽ tìm view mặc định tương ứng với ActionName hiện tại là "dang-ky" và khi đó thì chúng ta sẽ nhận được lỗi là không tìm thấy View.
     //  
     // GET: /Account/Register  
     [AllowAnonymous]  
     [ActionName("dang-ky")]  
     public ActionResult Register()  
     {  
       return View("Register");  
     }  

Chúng ta cũng cập nhật là action Login như đoạn code bên dưới.
     //  
     // GET: /Account/Login  
     [AllowAnonymous]  
     [ActionName("dang-nhap")]  
     public ActionResult Login(string returnUrl)  
     {  
       ViewBag.ReturnUrl = returnUrl;  
       return View("Login");  
     }  

Tiếp theo là chúng ta cần phải thay đổi lại ActionName trong các link đang gọi đến các action này.


Cuối cùng hãy Start Debugging (F5) để kiểm tra kết quả cuối cùng

Trang Đăng ký:
Trang Đăng nhập:


Các bạn có thể DOWNLOAD SOURCE CODE Ở ĐÂY

Đây là một số bước đơn giản để chúng ta có thể tạo ra SEO URL thân thiện người dùng trong ASP.NET MVC. Nếu có trao đổi hay thắc mắc thì các bạn có thể để lại comment bên dưới bài viết này.

Share to be shared.

Monday, June 27, 2016

Sử dụng Application_Error trong global.asax của ASP.NET MVC để xử lí Exception

ASP.NET MVC hỗ trợ chúng ta rất nhiều cách để handle và xử lí Exception. Ví dụ như:

- Try...catch...trực tiếp trong các action method.

- Override method OnException trong controller.

- Sử dụng thuộc tính HandleError hoặc chúng ta có thể tự tạo thuộc tính để xử lí lỗi thông qua việc kế thừa  HandleErrorAttribute.

- Xử lí HTTP Errors trong file Web.config.

- Xử lí Application_Error trong global.asax.

Tùy vào nhu cầu của mình mà chúng ta có thể xử lí exception theo một hay nhiều cách khác nhau. Trong bài viết này, Kiên sẽ hướng dẫn các bạn xử lí exception trong file global.asax.

Nếu bạn không muốn phải xử lí lỗi trong từng controller hay từng method, hoặc giả sử chúng ta bỏ sót không xử lí exception ở một nơi nào đó, hoặc chúng ta thích quản lý tập trung các exception, thì việc xử lí exception bằng cách khai báo method Application_Error trong file global.asax này khá là hữu dụng.

Ưu điểm khác là bạn có thể bắt tất cả các lỗi, đồng thời có thể thực hiện nhiều yêu cầu trong cùng một nơi. Ví dụ như khi có lỗi xảy ra, bạn muốn ghi log lại, đồng thời gửi mail hoặc thông báo đến admin, sau đó có thể redirect đến trang thông báo lỗi...

Cách thực hiện chức năng này khá đơn giản.

Đầu tiên chúng ta vào file global.asax và bổ sung thêm vào đó method Application_Error bởi vì ASP.NET MVC mặc định sẽ không có method này.

   public class MvcApplication : System.Web.HttpApplication  
   {  
     protected void Application_Start()  
     {  
       AreaRegistration.RegisterAllAreas();  
       GlobalConfiguration.Configure(WebApiConfig.Register);  
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
       RouteConfig.RegisterRoutes(RouteTable.Routes);  
       BundleConfig.RegisterBundles(BundleTable.Bundles);  
     }  

     void Application_Error(object sender, EventArgs e)  
     {  
       //get the last error of server  
       var error = Server.GetLastError();  

       //get the http code of error: 400, 404, 500...  
       var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;  

       //write log  

       //send email  

       //clear response stream  
       Response.Clear();  

       //clear server's error  
       Server.ClearError();  

       //redirect to error page  
       Response.Redirect(string.Format("~/Error/Index/{0}", code));  
     }  
   }  

Hãy nhìn vào Application_Error để biết những gì mà chúng ta cần phải làm:

1. Đầu tiên chúng ta lấy được lỗi mới xảy ra thông qua method Server.GetLastError. Đây có thể là bất kì lỗi nào chứ không nhất thiết chỉ là lỗi HttpException.
2. Khi đã bắt được lỗi thì việc tiếp theo là chúng ta có thể xử lí tùy ý. Ở đây Kiên sẽ ghi log lại để tiện trong việc tracking và đồng thời gửi mail đến cho mình.
3. Tiếp theo là chúng ta phải clear response stream để đảm bảo là tất cả nội dung được viết trong response phải được xóa hết.
4. Sau đó là chúng ta tiếp tục clear tất cả lỗi hiện tại đi.
5. Và cuối cùng là chúng ta sẽ điều hướng sang một trang mới dùng để hiển thị thông điệp cho người dùng, đồng thời truyền đi HTTP code để hiển thị thông tin phù hợp.

Bước tiếp theo là ta tạo ErrorController với action method có một tham số như bên dưới, và sau đó hãy nhớ add view cho nó.

   public class ErrorController : Controller  
   {  
     // GET: Error  
     public ActionResult Index(int id)  
     {  
       Response.StatusCode = id;  
       ViewBag.StatusCode = id;  
       return View();  
     }  
   }  

Vậy xem như chúng ta cơ bản đã hoàn thành việc xử lí lỗi thông qua Application_Error trong file global.asax. Hãy để lại comment bên dưới nếu bạn muốn trao đổi thêm!

Share to be shared!