Bài viết mới:

Thứ Sáu, 30 tháng 6, 2017

Hướng dẫn cài đặt plugin CKEditor và CKFinder trong ASP.NET MVC 5


Mình đã có bài viết Hướng dẫn tích hợp CKEditor và CKFinder trong ASP.NET trước đây, đó là bài viết hướng dẫn bạn cài đặt 2 plugin CKEditor và CKFinder vào ứng dụng ASP.NET Webform để hỗ trợ người dùng soạn thảo văn bản với đầy đủ những chức năng cần thiết giống phần mềm Microsoft Word. Ở bài này mình sẽ hướng dẫn bạn gắn 2 plugin này nhưng là cài đặt vào ứng dụng web ASP.NET MVC 5.

1. Cài đặt CKEditor


CKEditor là 1 plugin hỗ trợ người dùng soạn thảo văn bản trên website, điều đặc biệt là nó có nhiều package (gói) như Basic, Standard, Full, Customize bạn có thể xem chi tiết tại địa chỉ sau http://ckeditor.com/download. Trong bài viết này mình sẽ hướng dẫn bạn cài đặt gói Full với đầy đủ những chức năng để soạn thảo văn bản, với các gói khác thì cách làm cũng tương tự.

Bước 1: Truy cập địa chỉ http://ckeditor.com/download và click Download ở gói Full Package. 

Bước 2: Sau khi tải về bạn sẽ có được 1 file .zip. Tiến hành giải nén file này và sao chép thư mục ckeditor và dán vào thư mục gốc project của bạn. Kết quả được như hình dưới.


Bước 3: Trong view mà bạn muốn sử dụng ckeditor bạn cần khai báo đoạn script sau: 
<script  src="~/ckeditor/ckeditor.js"></script>

<script>
        CKEDITOR.replace("IDCuaTextArea");
</script>

Bạn thay IDCuaTextArea thành giá trị ID của tag <textarea></textarea> mà bạn muốn CKEditor sẽ ghi đè lên tag đó.

Ví dụ tôi có tag <textarea id="NoiDung" name="NoiDung" ></textarea> thì đoạn lệnh của tôi sẽ như sau


<script>
        CKEDITOR.replace("NoiDung");
</script>

Vậy là hoàn thành. Bạn thử kiểm tra xem có đạt kết quả như hình dưới đây không nhé.




2. Tích hợp CKFinder vào CKEditor


Dù gói FULL của CKEditor có khá nhiều chức năng nhưng nó còn thiếu 1 chức năng theo mình thấy là cực kỳ quan trọng đó là upload hình ảnh và tập tin từ máy tính người dùng vào bài viết. Để CKEditor hỗ trợ thêm chức năng ấy thì chúng ta cần phải tích hợp thêm CKFinder vào CKEditor. 
CKFinder thực chất là 1 add-on hay có thể hiểu là 1 tiện ích bổ sung thêm của CKEditor. 

Đối với CKFinder thì bạn phải trả phí để mua nếu không thì chỉ có thể xài được bản demo tuy nhiên bài viết này mình sẽ hướng dẫn bạn sử dụng tránh được việc mất phí nhưng vẫn sử dụng được bản đầy đủ của CKFinder bằng cách dùng Keygen.

Bước 1: Sau khi cài đặt CKEditor thành công bạn tiến hành truy cập địa chỉ https://cksource.com/ckfinder, sau đó click tab "Free trial" và và click vào hình Microsoft ASP.net.



Bước 2: Tại tab này bạn kéo xuống tải file zip for ASP.NET của CKFinder version 2.6.2.1 



Bước 3: Sau khi tải file .zip về bạn tiến hành giải nén và sao chép thư mục ckfinder sau đó dán vào thư mục gốc project của bạn. Kết quả sẽ được như hình dưới.



Tiếp theo bạn sao chép toàn bộ file trong thư mục ckfinder/bin/Debug vào thư mục bin ở project của bạn nhé.

Bước 4: Mở file config.ascx trong thư mục ckfinder ở project. Thay lệnh return false; thành return true; trong method CheckAuthentication()

Sau đó tìm đến method SetConfig() bạn sẽ thấy có 2 thuộc tính LicenseNameLicenseKey đây là 2 thuộc tính để xác định bản quyền của CKFinder. Bây giờ mình hướng dẫn bạn lấy 2 giá trị này bằng cách truy cập vào website https://huytq.me/tools/ckfinder-keygen.html.

Sau khi lấy được 2 giá trị License Name và License Key từ website trên thì bạn dán vào giá trị 2 thuộc tính LicenseName và LicenseKey là xong.

Ví dụ LicenseName và LicenseKey của mình

Còn nữa, phía dưới 2 thuộc tính LicenseName và LicenseKey là thuộc tính BaseUrl. Thuộc tính này chỉ định nơi các chứa file mà người dùng upload. Bạn có thể thay đổi theo ý mình. Giả sử nếu tôi muốn lưu vào thư mục Photos ở thư mục gốc của project thì tôi sửa giá trị thành "/Photos/".

Bước 5: Tại view, bạn khai báo thêm thẻ script sau <script type="text/javascript" src="~/ckfinder/ckfinder.js"></scriptvà bổ sung lệnh CKFinder.setupCKEditor(null,'/ckfinder'); dưới lệnh CKEDITOR.replace("IDTextArea");

Thông qua 5 bước trên là bạn đã hoàn thành tích hợp thêm add-on CKFinder vào CKEditor rồi đó. Cách upload hình ảnh rất đơn giản thôi, bạn click vào button Image trên CKEditor sau đó chọn tab Tải lên để chọn file cần upload và thực hiện upload lên server.



Giả sử sau này bạn muốn dùng lại file đã upload trước đó thì có thể click button "Duyệt máy chủ" để lấy những file đã upload trước đó.


Chú ý: nếu khi build website mà bị lỗi liên quan đến 2 plugin này thì bạn hãy exclude 2 folder ckeditor và ckfinder khỏi project chứ ko cần include vào project nhé.

Chúc bạn thành công.

Thứ Tư, 28 tháng 6, 2017

Hướng dẫn gắn "Flexible Responsive jQuery Image Slider Plugin" vào website



Bài viết này hướng dẫn bạn từng bước gắn Flexible Responsive jQuery Image Slider Plugin vào website của bạn. Đây là một plugin jquery có hiệu ứng rất mượt và hỗ trợ responsive tốt. Bạn có thể xem ngay demo về plugin này tại địa chỉ sau http://www.jqueryscript.net/demo/Flexible-Responsive-jQuery-Image-Slider-Plugin-Simple-Slider/.


Sau đây là các bước thực hiện:

 Bước 1: Tạo thư mục css, sau đó tạo file slider.css  trong thư mục này và dán đoạn lệnh bên dưới vào file slider.css.

/* ==========================================================================
        Slider core styles
===========================================================================*/
*,
*:before,
*:after {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}

.slider-container {
  position: relative;

  overflow: hidden;

  width: 100%;
  margin: 0 auto;
}

.slider {
  position: relative;

  width: 9999px;
}
.slider:before,
.slider:after {
  display: table;

  content: ' ';
}
.slider:after {
  clear: both;
}
.slider div {
  position: relative;

  float: left;

  margin: 0;
  padding: 0;
}
.slider div img {
  display: block;

  max-width: 100%;
  height: auto;
}
.act,
#prev > span,
#next > span,
#slider-nav > a {
  background: url(../images/arrows.png) no-repeat;
}
#next > span,
#prev > span {
  display: block;

  width: 16px;
  height: 24px;
}
/* Arrows */
#next > span {
  background-position: -20px 0;
}
#prev > span {
  background-position: 0 0;
}
#next,
#prev {
  position: absolute;
  top: 50%;

  margin-top: -20px;
  padding: 10px 15px;

  cursor: pointer;
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;

  border-radius: 5px;
  background: rgba(0, 0, 0, .39);

  filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#99000000', endColorstr='#99000000'); /* ie8 */
       -o-user-select: none;
}
#prev {
  left: 10px;
}
#next {
  right: 10px;
}
.caption {
  position: absolute;
  bottom: 0;

  display: block;

  width: 100%;
  padding: 0 10px 30px;

  color: #fff;
  background: rgba(0, 0, 0, .39);

  filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#99000000', endColorstr='#99000000'); /* ie8 */
}
.caption a {
  display: block;

  color: #fff;
}
.slider-nav {
  line-height: 30px;

  position: absolute;
  bottom: 0;
  left: 0;

  width: 100%;
  height: 30px;
  margin: 0;
  padding: 0;

  text-align: center;
  /*background: #323232;*/
  /*opacity: .9;*/

  filter: alpha(opacity=90); /* ie8 */
}
.slider-nav a {
  display: inline-block;

  width: 12px;
  height: 12px;
  margin: 0 3px;

  -webkit-transition: background .5s ease;
     -moz-transition: background .5s ease;
       -o-transition: background .5s ease;
          transition: background .5s ease;

  border: 2px solid #fff;
  border-radius: 50%;
  background: transparent;
}

.slider-nav .active {
  width: 12px;
  height: 12px;

  background: #fff;
}
@media (max-width: 580px) {
  .caption {
    display: none;
  }
}

Bước 2: Tạo folder js, sau đó tạo file slider.js và dán đoạn lệnh sau vào file slider.js

/* jQuery simple slider */
(function($) {

"use strict";

var
supportCss3 = function( value ) {
    var el = document.createElement('div');
    switch(value) {
        case "transform":
            var prefix = {
                transform       : "transform",
                webkitTransform : "-webkit-transform",
                mozTransform    : "-moz-transform",
                msTransform     : "-ms-transform",
                oTransform      : "-0-transform"
            }
        break;
        case "transition":
            var prefix = {
                transition       : 'transition',
                webkitTransition : 'webkitTransition',
                mozTransition    : 'mozTransition',
                oTransition      : 'oTransition'
            }
        break;
    }
    for(var name in prefix) {
        if(el.style[name] !== undefined) {
            return prefix[name];
        }
    }
    return false;
},
transform  = supportCss3("transform"),
transition = supportCss3("transition");


$.fn.sliderUi = function(o) {
    o = $.extend({
        autoPlay: true,
        delay: 3000,
        controlShow: true,
        arrowsShow: true,
        caption: false,
        speed: 300,
        cssEasing: "ease-out"
    }, o || {});

    return this.each(function() {
        var
            container    = $(this),
            slider       = container.find(".slider"),
            arrows       = container.find(".switch"),
            caption      = slider.find(".caption"),
            img          = slider.find("img"),
            imgLen       = img.length,
            imgWidth     = container.outerWidth(true),
            sliderWidth  = imgLen * imgWidth,
            controlPanel = null,
            current      = 0,
            offset       = null,
            busy         = false,
            timer        = null;

        slider.css("width", sliderWidth + "px");
        img.width( imgWidth );
        slider.show();

        $(window).on("resize", function() {
            if(transition) {
                slider.css(transition, "none");
            }
            imgWidth     = container.width();
            sliderWidth  = imgLen * imgWidth;
            img.width( imgWidth );
            if(transition && transform) {
                slider.css({
                    width: sliderWidth + "px",
                    transform: "translateX("+ -(imgWidth*current) + "px)"
                });
            } else {
                slider.css({
                    width: sliderWidth + "px",
                    "margin-left": -(imgWidth*current) + "px"
                });
            }
        })

        !o.caption && caption.remove();

        if( o.controlShow) {
            controlPanel = $("<div/>", {
                "class": "slider-nav"
            })
            .appendTo(container);

            // Control links
            var links = [];
            for(var i = 0; imgLen > i; i++) {
                var act = (current === i) ? "active" : "";
                    links.push("<a class='"+act+"' data-id='"+i+"'></a>");
            }
            controlPanel.get(0).innerHTML = links.join("");

            var navControl = controlPanel.find("a");
            navControl.on("click", function(e) {
                e.preventDefault();
                current = this.getAttribute("data-id");
                show("current");
            })
        }

        var show = function(side) {
            if(busy) return;

            if(side === "next") {
                if(current < imgLen - 1) {
                    offset = - (imgWidth*(++current)) + "px";
                }
                else {
                    offset = 0;
                    current  = 0;
                }
            }
            else if(side === "current") {
                offset = - (imgWidth*current) + "px";
            }
            else {
                if(current > 0) {
                    offset = - (imgWidth*(--current)) + "px";
                }
                else {
                    offset = - (imgWidth*(imgLen - 1)) + "px";
                    current  = imgLen -1;
                }
            }
            if(o.controlShow) {
                navControl.removeClass("active");
                navControl.eq(current).addClass("active");
            }

            if(transition && transform) {
                slider.css({
                    transition: transform + " " + o.speed + "ms " + o.cssEasing,
                    transform: "translateX(" + offset + ")"
                })
            }
            else {
                busy = true;
                slider.animate({"margin-left": offset}, o.speed, "linear", function() {
                    busy = false;
                })
            }
        }

        if(o.arrowsShow) {
            arrows.on("click", function(e) {
                e.preventDefault();
                var side = this.id;
                show(side);
            })
        }
        else {
            arrows.remove();
        }

        var auto = function() {
            if(timer) clearInterval(timer);
            timer = setInterval(function() {
                show("next");
            }, o.delay);
        }

        if(o.autoPlay) {
            auto();
            container.hover(function() {
                clearInterval(timer);
            }, function() {
                auto();
            });
        }

    });

}

})(jQuery);


 Bước 3: Khai báo lệnh sau trong thẻ <head></head> ở trang web của bạn.

<link href="css/slider.css" rel="stylesheet">

 Bước 4: Khai báo đoạn script sau ở cuối thẻ <body> ở trang web của bạn

<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
          <script src="js/slider.js"></script>
          <script>
            $("#demo").sliderUi({

            // Auto play when page loads
            autoPlay: true,

            // animation delay in ms
            delay: 3000,

            // display controls
            controlShow: true,

            // display arrows navigation
            arrowsShow: true,

            // display image captions
            caption: false,

            // animation speed
            speed: 300,

            // CSS3 easing effects
            cssEasing: "ease-out"

            });
          </script>


 Bước 5: Dán đoạn lệnh HTML sau vào nơi bạn muốn đặt plugin

       <div class="slider-container" id="demo">
                <!-- <a href="http://www.jqueryscript.net/slider/">Slider</a>  -->
                <div class="slider">
                    <div>
                    <img src="images/1.jpg" alt="">
                    <span class="caption">Image Caption 1</span>
                    </div>
                    <div>
                    <img src="images/2.jpg" alt="">
                    <span class="caption">Image Caption 2</span>
                    </div>
                    <div>
                    <img src="images/3.jpg" alt="">
                    <span class="caption">Image Caption 3</span>
                    </div>
                </div>
               
                <!-- Controls -->
                <div class="switch" id="prev"><span></span></div>
                <div class="switch" id="next"><span></span></div>

          </div>

Chú ý: 3 giá trị ở attribute src images/1.jpg, images/2.jpg, images/3.jpg chỉ là mình ví dụ. Bạn tự lấy hình và sửa đường dẫn cho đúng nhé. Chú ý rằng 3 hình ảnh có size bằng nhau thì sẽ đẹp nhất.

Thứ Bảy, 20 tháng 5, 2017

Tìm hiểu về method RenderBody(), RenderSection() trong Asp.net MVC

1. Tìm hiểu về layout view


Một website có thể chứa các phần chung trong các giao diện người dùng mà vẫn giữ nguyên trong suốt ứng dụng chẳng hạn như logo, tiêu đề, thanh điều hướng bên trái,thanh điều hướng bên phải hoặc phần footer đó được gọi là layout web.

ASP.NET MVC giới thiệu Layout view là một view mà có thể dùng lại trong nhiều view khác để cung cấp một giao diện nhất quán trong nhiều trang của một site. Layout View giống như master page của ứng dụng ASP.NET Webform.

Ví dụ, một giao diện người dùng có thể chứa header, thanh menu trái, thanh menu phải và phần chân trang mà vẫn giống nhau trong tất cả các trang và chỉ có phần trung tâm thay đổi tự động theo từng trang trong website như hình dưới đây.



Khi tạo ứng dụng ASP.NET MVC, mặc định file layout có tên _Layout.cshtml trong thư mục Views/Shared.

File _Layout.cshtml giống với view khác ngoại trừ việc trong Layout view có chứa 2 lệnh gọi method @RenderBody và @RenderSection.

Layout chứa một và chỉ một lệnh @RenderBody() để giữ chỗ cho nội dung trong những view kế thừa layout.

Layout không có hoặc nhiều lệnh @RenderSection() để giữ chỗ cho những thành phần khác được đánh dấu thông qua khối @section tensection { } trong view kế thừa layout.


2. RenderBody()


Tất cả nội dung của những view mà kế thừa 1 layout nào đó sẽ chứa trong RenderBody().

3. RenderSection()


Hiển thị nội dung của 1 thành phần nhỏ trong layout


Ví dụ về RenderSection


Method RenderSection() có 2 tham số tham số thứ nhất là tên section tham số thứ 2 là required kiểu bool. Nếu các bạn không muốn sử dụng RenderSection trong tất cả các trang web thì thì truyến giá trị false vào tham số required.

Chủ Nhật, 14 tháng 5, 2017

Tìm hiểu LINQ và sử dụng LINQ trên C#


1.  Giới thiệu về LINQ


Language-Integrated Query (LINQ) là một sự đổi mới được giới thiệu trong Visual Studio 2008 và .NET Framework phiên bản 3.5.

Theo truyền thống, các truy vấn dữ liệu được thể hiện dưới dạng các chuỗi mà không cần kiểm tra kiểu tại thời gian biên dịch hoặc hỗ trợ IntelliSense và bạn phải học một ngôn ngữ truy vấn khác nhau cho từng loại nguồn dữ liệu ví dụ như: cơ sở dữ liệu SQL, tài liệu XML, dịch vụ web khác nhau, ... LINQ giúp nhúng truy vấn vào ngôn ngữ lập trình (đúng như tên gọi của nó). Tức là bạn có thể sử dụng C# hay Visual Basic để có thể truy vấn đến mọi nguồn dữ liệu mà không nhất thiết phải biết các loại ngôn ngữ truy vấn riêng biệt để có thể thao tác với chúng. Bạn sẽ viết truy vấn sử dụng từ khóa và toán tử quen thuộc của ngôn ngữ lập trình. Hình minh họa dưới đây cho thấy một truy vấn LINQ kết nối đến 1 cơ sở dữ liệu SQL Server trong C# và Visual Basic:




Trong Visual Studio, bạn có thể viết các truy vấn LINQ trong Visual Basic hoặc C# đến cơ sở dữ liệu SQL Server, các tài liệu XML, tập dữ liệu ADO.NET, và bất kỳ collections của của đối tượng có hỗ trợ interface IEnumerable hoặc IEnumerable<T>. LINQ cũng hỗ trợ cho ADO.NET Entity Framework, và các LINQ provider đang được viết bởi bên thứ ba cho nhiều dịch vụ Web và triển khai cơ sở dữ liệu khác.

Để sử dụng LINQ thì dự án của bạn phải đang chạy trên nền tảng .NET Framework 3.5 hoặc cao hơn.

2.  Sử dụng LINQ trên C#


a. Giới thiệu về LINQ Queries (C#)


Một truy vấn là một biểu thức để lấy dữ liệu từ một nguồn dữ liệu. Các truy vấn thường được biểu thị bằng một ngôn ngữ truy vấn cụ thể. Trước đây với mỗi nguồn dữ liệu thì sẽ đi kèm với ngôn ngữ riêng để truy vấn trên nguồn dữ liệu đó, ví dụ SQL cho cơ sở dữ liệu quan hệ và XQuery cho XML. Do đó, các nhà phát triển đã phải học một ngôn ngữ truy vấn mới cho mỗi loại nguồn dữ liệu. LINQ đơn giản hoá vấn đề này bằng cách tích hợp truy vấn vào trong ngôn ngữ lập. Trong một truy vấn LINQ, bạn luôn làm việc với các đối tượng (object). Bạn sử dụng cùng một truy vấn và chuyển đổi dữ liệu trong các tài liệu XML, cơ sở dữ liệu SQL, ADO.NET Datasets, .NET collections và bất kỳ định dạng nào khác mà LINQ provider có sẵn. 

Truy vấn LINQ chia thành 3 thành phần sau:
  1. Chuẩn bị nguồn dữ liệu
  2. Tạo câu lệnh truy vấn LINQ
  3. Thực thi truy vấn để lấy kết quả

Ví dụ sau đây cho thấy cách hoạt động của 3 thành phần trên. Ví dụ sử dụng một mảng số nguyên như là một nguồn dữ liệu. Ví dụ này được sử dụng trong suốt phần còn lại của chủ đề này.

      
        //  1. Nguồn dữ liệu.
        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

        // 2. Tạo câu lệnh truy vấn:
        // Lấy ra các số chẵn trong mảng numbers
        var numQuery =
            from num in numbers
            where (num % 2) == 0
            select num;

        // 3. Thực thi truy vấn.
        foreach (int num in numQuery)
        {
            Console.Write("{0,1} ", num);
        }

Hình minh họa dưới đây cho thấy cách hoạt động của LINQ.



Các biểu thức truy vấn có chứa ba mệnh đề: from, where, select. (Nếu bạn đã quen thuộc với SQL, bạn sẽ nhận thấy rằng cách sắp xếp trật tự của các mệnh đề ngược lại với trật tự trong SQL). Mệnh đề from chỉ ra nguồn dữ liệu, mệnh đề where áp dụng để lọc dữ liệu theo 1 điều kiện nào đó, và mệnh đề select chỉ ra kiểu của kết quả trả về. Những mệnh đề này và một số mệnh đề khác được thảo luận chi tiết trong phần LINQ Query Expressions (C# Programming Guide).

Chú ý: Truy vấn LINQ cũng có thể được xây dựng bằng cách sử dụng phương thức mở rộng (method systax) thay vì  dùng theo kiểu Query syntax như ví dụ. Để biết thêm thông tin, xem Query Syntax and Method Syntax in LINQ (C#).

Trì hoãn thực thi


Các biến truy vấn mặc định chỉ lưu trữ các lệnh truy vấn. Các hoạt động truy vấn được hoãn lại cho đến khi bạn lặp qua các biến truy vấn trong một câu lệnh foreach. Khái niệm này được gọi là deferred execution (trì hoãn thực thi) và được thể hiện trong ví dụ sau:

//Khi vòng lặp được chạy thì truy vấn mới được thực thi và gán kết quả truy vấn qua biến num.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}


Câu lệnh foreach cũng là nơi mà các kết quả truy vấn được lấy ra. Ví dụ, trong truy vấn trên, mỗi lần lặp thì biến num giữ mỗi giá trị khác nhau (tại mỗi thời điểm) bởi vì mỗi lần lặp là mỗi lần trình biên dịch thực thi query và lấy dữ liệu từ CSDL.

Buộc thực thi ngay


Truy vấn thực hiện chức năng thống kê phải duyệt qua những phần tử. Ví dụ như các truy vấn Count, Max, Min, Sum, Average, First. Đó là những truy vấn mà sự thực thi truy vấn đó mà không có một câu lệnh foreach rõ ràng vì các truy vấn tự nó phải sử dụng ngầm foreach để trả lại kết quả. Ví dụ truy vấn dùng mệnh đề Sum sẽ ngầm dùng vòng lặp foreach để duyệt qua tất cả các phần tử để thực hiện tính tổng. Lưu ý rằng các loại truy vấn thống kê sẽ trả về 1 giá trị duy nhất do đó kiểu trả về không phải là 1 queryable. Các truy vấn sau đây trả về số lượng các số chẵn trong mảng numbers:

var evenNumQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

int evenNumCount = evenNumQuery.Count();

Buộc thực thi ngay và lưu lại kết quả vào 1 collection hoặc generic collection, bạn có thể sử dụng phương thức ToList<TSource> hoặc ToArray<TSource> . Ví dụ:

List<int> numQuery2 =
    (from num in numbers
     where (num % 2) == 0
     select num).ToList();

// hoặc dùng cách này:
// numQuery3 sẽ có kiểu là 1 mảng int[]

var numQuery3 =
    (from num in numbers
     where (num % 2) == 0
     select num).ToArray();


Bằng cách gọi ToList hoặc ToArray bạn sẽ lưu trữ tất cả các dữ liệu trong một đối tượng collection nằm trong bộ nhớ để sử dụng dữ liệu đó và truy vấn tiếp về sau trên collection thay vì cứ phải truy xuất đến dữ liệu mỗi khi thực thi truy vấn.

b. LINQ và kiểu Generic


Truy vấn LINQ dựa trên các kiểu generic, mà được giới thiệu trong .NET Framework 2.0. Bạn không cần 1 kiến thức chuyên sâu về Generic để có thể bắt đầu viết truy vấn LINQ. Tuy nhiên, bạn cần phải biết 2 khái niệm cơ bản sau đây:
  1. Khi bạn tạo một đối tượng của một 1 class Generic Collection như List<T>, bạn sẽ thay thế chữ “T” với chính xác kiểu dữ liệu của đối tượng mà danh sách giữ. Ví dụ, List<string> đại diện cho một danh sách chuỗi hoặc 1 danh sách các Customer có thể được khai báo là List<Customer>. 
  2. IEnumerable<T> là interface cho phép các lớp Collection Generic có thể dùng câu lệnh lặp foreach. Các lớp Generic Collection hỗ trợ IEnumerable<T> cũng giống như các lớp non-generic như ArrayList hỗ trợ IEnumerable .
Để biết thêm thông tin về generics, xem Generics (C# Programming Guide).

c. Sử dụng kiểu var


Nếu bạn thích, bạn có thể tránh được cú pháp generic bằng từ khóa var. Từ khóa var chỉ thị trình biên dịch tự định kiểu theo giá trị được gán. Tuy nhiên nếu truy vấn LINQ trả về 1 kiểu vô danh (anonymos) thì bạn bắt buộc phải dùng kiểu var.

Ví dụ 1 (có thể dùng var):


    var customerQuery =
    from cust in customers
    where cust.City == "London"
    select cust;

Ví dụ 2 (bắt buộc dùng var):


    var customerQuery =
    from cust in customers
    where cust.City == "London"
    selet new { ID = cust.ID, CityName = cust.City };   // truy vấn trả về kiểu vô danh

3. Truy vấn cơ bản LINQ (C#)


Chủ đề này giới thiệu ngắn gọn các biểu thức truy vấn LINQ và một số mệnh đề điển hình mà bạn thực hiện trong một truy vấn. thông tin chi tiết hơn có trong các chủ đề sau:

LINQ Query Expressions (C# Programming Guide)
Standard Query Operators Overview
Walkthrough: Writing Queries in C# (LINQ)

Lấy 1 nguồn dữ liệu 


Trong một truy vấn LINQ, bước đầu tiên là xác định nguồn dữ liệu. Trong C# và hầu hết các ngôn ngữ lập trình khác một biến phải được khai báo trước khi nó có thể được sử dụng. Trong một truy vấn LINQ, mệnh đề from được viết đầu tiên để khai báo nguồn dữ liệu, đi kèm là biến phạm vi.

Trong ví dụ dưới đây nguồn dữ liệu là customers và biến phạm vi là cust: 

//queryAllCustomers is an IEnumerable<Customer>
var queryAllCustomers = from cust in customers
                        select cust;


Biến phạm vi giống như các biến lặp trong một vòng lặp foreach ngoại trừ việc không có 1 sự lặp lại thực sự xảy ra trong một biểu thức truy vấn. Khi truy vấn được thực thi, biến phạm vi sẽ có nhiệm vụ như là một tham chiếu đến từng phần tử kế tiếp trong customers. Bởi vì trình biên dịch có thể suy ra kiểu của biến cust , bạn không cần phải xác định kiểu tường minh.

Lọc (Filter)


Có lẽ 1 trong các hoạt động truy vấn phổ biến nhất là áp dụng một bộ lọc. Bộ lọc khiến cho truy vấn trả về chỉ những phần tử thỏa yêu cầu. Sử dụng mệnh đề where kèm theo một biểu thức boolean để thực hiện lọc. Trong ví dụ sau đây, chỉ những custommers có địa chỉ ở London được trả về.

var queryLondonCustomers = from cust in customers
                           where cust.City == "London"
                           select cust;

Bạn có thể sử dụng toán tử AND (&&)OR (||) quen thuộc trong C# để áp dụng nhiều biểu thức lọc khi cần thiết. Ví dụ, để trả về chỉ có khách hàng từ "London" có tên là "Devon" bạn sẽ viết đoạn code sau:

var queryLondonCustomers = from cust in customers
                           where cust.City=="London" && cust.Name == "Devon"
                           select cust;

Trả về chỉ những khách hàng đến từ London hoặc Pari bạn sẽ viết đoạn code sau:

var queryLondonCustomers = from cust in customers
                                                      where cust.City == "London" || cust.City == "Pari"                               select cust;

Để biết thêm thông tin, xem where clause (C# Reference)

Sắp xếp (Sorting)


Các mệnh đề orderby mặc định sẽ sắp xếp phần tử theo thứ tự tăng dần . Ví dụ, truy vấn sau đây thực hiện sắp xếp danh sách customers theo thứ tự tăng dần theo Name.

    var queryLondonCustomers3 =
    from cust in customers
    where cust.City == "London"
    orderby cust.Name ascending
    select cust;

Ngược lại, để sắp xếp giảm dần sử dụng mệnh đề orderby…descending

    var queryLondonCustomers3 =
    from cust in customers
    where cust.City == "London"
    orderby cust.Name descending
    select cust;

Gom nhóm (Group)


Mệnh đề group cho phép bạn nhóm kết quả trả về dựa trên một khóa (key) mà bạn chỉ định. Ví dụ bạn có thể chỉ định kết quả trả về sẽ được nhóm theo City để có thể phân các khách hàng ở London và Paris thành các nhóm riêng. Trong trường hợp này Cust.City là 1 khóa (key).

  // queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>>
  var queryCustomersByCity =
      from cust in customers
      group cust by cust.City;

  // customerGroup is an IGrouping<string, Customer>
  foreach (var customerGroup in queryCustomersByCity)
  {
      Console.WriteLine(customerGroup.Key);
      foreach (Customer customer in customerGroup)
      {
          Console.WriteLine("    {0}", customer.Name);
      }
  }


Khi bạn kết thúc truy vấn với mệnh đề group, kết quả trả về của bạn mang hình thức như một danh sách của danh sách.

Nếu bạn cần tham chiếu các kết quả của một group operation, bạn có thể sử dụng từ khóa into để tạo ra một định danh có thể được truy vấn thêm. Các chỉ truy vấn sau trả về những nhóm có chứa nhiều hơn hai khách hàng.

// custQuery is an IEnumerable<IGrouping<string, Customer>>
  var custQuery =
    from cust in customers
    group cust by cust.City into custGroup
    where custGroup.Count() > 2
    orderby custGroup.Key
    select custGroup;
  }

Để biết thêm thông tin, xem group clause (C# Reference).

Nối (Joining)


Sử dụng mệnh đề join để có thể trả về 1 kết quả được tổng hợp từ nhiều nguồn lại với nhau. Ví dụ, bạn có thể sử dụng join để tìm tất cả các khách hàng và nhà phân phối có cùng một vị trí.

    var innerJoinQuery =
    from cust in customers
    join dist in distributors on cust.City equals dist.City
    select new { CustomerName = cust.Name, DistributorName = dist.Name };


Để biết thêm thông tin, xem join clause (C# Reference).

Chọn (Selection)


Mệnh đề select xác định kiểu trả về của truy vấn LINQ. Ví dụ, bạn có thể xác định xem kết quả của bạn sẽ trả về 1 đối tượng Customer hoàn chỉnh với đầy đủ các thuộc tính hoặc một số loại kết quả hoàn toàn khác nhau dựa trên tính toán hay tạo đối tượng mới.

Khi bạn dùng mệnh select để trả về 1 kiểu dữ liệu mới thì đó được gọi là chiếu( projection). Việc sử dụng projection để chuyển đổi dữ liệu là một khả năng mạnh mẽ của các biểu thức truy vấn LINQ. 

Để biết thêm thông tin, xem Data Transformations with LINQ (C#)select clause (C# Reference).

4. Lời kết


LINQ là 1 công nghệ rất mạnh trong .NET Framework, nhờ LINQ thì việc truy vấn dữ liệu từ bất cứ nguồn dữ liệu nào đều dễ dàng và thống nhất 1 cú pháp chung. Người ta ví rằng 1 developer học C# nhưng không biết đến LINQ cũng giống như khi bạn sang Trung Quốc nhưng không đến Vạn Lý Trường Thành :)). Hi vọng qua biết viết này đã giúp bạn hiểu rõ về LINQ và sử dụng thành thạo nó trong nhiều dự án sắp tới. 

Thứ Năm, 11 tháng 5, 2017

Sử dụng class NOCSRF để ngăn chặn tấn công CSRF trong PHP


Giới thiệu


Khi xây dựng 1 ứng dụng hoặc 1 website sử dụng PHP, bạn cần quan tâm đến vấn đề bảo mật.

Một trong nhiều cách thức tấn công được sử dụng bởi những tin tặc (hackers) là Cross-Site Request Forgery (CSRF) chúng ta có thể hiểu là tấn công bằng cách mạo danh 1 website hay ứng dụng nào đó. Để hiểu rõ về CSRF thì bạn có thể đọc bài viết SERIES BẢO MẬT NHẬP MÔN – CSRF – NHỮNG CÚ LỪA NGOẠN MỤC của tác giả Phạm Huy Hoàng.

Bài viết này sẽ hướng dẫn bạn sử dụng class NOCSRF, 1 class đơn giản viết bằng PHP5 nhằm phòng chống CSRF.

Download


Trước hết bạn tải class NOCSRF ở địa chỉ https://github.com/BKcore/NoCSRF

Sử dụng


Bước 1: Giả sử mình có file test.php và giờ mình muốn dùng class NOCSRF trong file đó thì nội dung bên trong file sẽ như sau:

// Token được lưu trữ ở session nên bạn phải khởi tạo session
session_start();
// Khai báo file nocsrf.php từ mã nguồn tải về. Đường dẫn bạn có thể thay đổi tùy
// vào nơi bạn đặt file nocsrf.php
require_once('nocsrf.php');

Bước 2: Sử dụng method NoCSRF::generate() để sinh ra 1 token và gán qua biến $token

$token = NoCSRF::generate( 'csrf_token' );



 Giá trị chuỗi 'csrf_token' thực chất là key của session sẽ được tạo ra.

Bước 3: Cuối cùng bạn đặt giá trị $token vào thẻ input hidden trong form của bạn

<form name="csrf_form" action="#" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo $token; ?>">
    ...Other form inputs...
    <input type="submit" value="Send form">
</form>

Bước 4: Bên code xử lý khi form được submit bạn đặt khối try catch như sau:

try
{
    NoCSRF::check( 'csrf_token'$_POST, true, 60*10, false );
    // lệnh của bạn
}
catch ( Exception $e )
{
    // Bị tấn công CSRF
}

Method NoCSRF::check() để kiểm tra tính hợp lệ của token. Cú pháp tổng quát của method này như sau:

NoCSRF :: check ($key, $origin [, $throwException [, $timespan [, $multiple]]]) 



  • Tham số $key (String): key của session lưu giữ token.
  • Tham số $origin (Mixed): đối tượng/mảng để truy xuất dữ liệu token (thường là $_POST)
  • Tham số $throwException (Boolean): ném ngoại lệ hoặc không ném ngoại lệ khi kiểm tra thất bại. Nếu gán true tức là sẽ ném ra ngoại lệ, false thì method sẽ trả về giá trị Boolean. Ở ví dụ này mình truyền giá trị true vào nên chúng ta mới xài khối try catch, nếu bạn truyền vào là false thì ko dùng try catch mà bạn gán giá trị trả về của method check qua 1 biến và kiểm tra xem biến đó giá trị true hay false mà xử lý.
  • Tham số $timespan (Integer): Giá trị token sẽ hết hạn sau 1 khoảng thời gian (giây). Mặc định là không bao giờ. 
  • Tham số $multiple (Boolean): Tái sử dụng token hoặc không (nên gán true nếu là 1 yêu cầu ajax nặng)
Ở ví dụ này mình chỉ định token được lưu giữ 60*10 s = 10 phút, dùng cách ném ngoại lệ khi lỗi kiểm tra token và không tái sử dụng token.  


Nếu nhảy vào khổi catch tức bạn đang bị tấn công CSRF từ 1 người dùng mạo danh website của bạn. Bạn có thể viết code xử lý tùy ý ví dụ như thông báo lỗi hoặc chuyển hướng người dùng đó về trang chủ.

Kiểm tra


Để kiểm tra xem mọi thứ đã hoạt động tốt chưa thì bạn có thể sử dụng POSTMAN để gửi 1 yêu cầu giả mạo đến trang web của bạn. 

Chúc bạn cài đặt thành công.