简体   繁体   中英

jquery datatables server side columns

I am using jquery datatables with server side C#/asp.net mvc3 processing. it all works now but i have a new task to do:

i want to hide some columns for security reasons to certain users. how do i create columns in server side c# and pass to datatable in the view?

Controller might look like:

public JsonResult NewsJSON(jQueryDataTableParamModel param)
    {
        var news = _newsRepository.GetNews();

        string col = param.sColumns.Split(',')[param.iSortCol_0];
        string orderby = col + " " + param.sSortDir_0;

        if (param.sSearch != null)
            news = news.Search(param.sSearch);

        var qry = new PaginatedList<News>(news.OrderBy(orderby), param.iDisplayStart, param.iDisplayLength);

        return Json(new
        {
            sEcho = param.sEcho,
            iTotalRecords = qry.TotalCount,
            iTotalDisplayRecords = qry.TotalCount,
            aaData = (
                from n in qry
                select new[]
                {
                    n.Headline, 
                    n.DateCreated.ToString() 
                }).ToArray()
        }, JsonRequestBehavior.AllowGet);
    }

jQueryDataTableParamModel.cs

public class jQueryDataTableParamModel
{
    /// <summary>
    /// Request sequence number sent by DataTable,
    /// same value must be returned in response
    /// </summary>       
    public string sEcho { get; set; }

    /// <summary>
    /// Text used for filtering
    /// </summary>
    public string sSearch { get; set; }

    /// <summary>
    /// Number of records that should be shown in table
    /// </summary>
    public int iDisplayLength { get; set; }

    /// <summary>
    /// First record that should be shown(used for paging)
    /// </summary>
    public int iDisplayStart { get; set; }

    /// <summary>
    /// Number of columns in table
    /// </summary>
    public int iColumns { get; set; }

    /// <summary>
    /// Number of columns that are used in sorting
    /// </summary>
    public int iSortingCols { get; set; }

    /// <summary>
    /// Comma separated list of column names
    /// </summary>
    public string sColumns { get; set; }

    /// <summary>
    /// Sort column
    /// </summary>
    public int iSortCol_0 { get; set; }

    /// <summary>
    /// Asc or Desc
    /// </summary>
    public string sSortDir_0 { get; set; }

}

And in view:

<script type="text/javascript" charset="utf-8">

$(function () {

$('#news').dataTable({    
"bSort" : true,        
"bServerSide": true,    
"sAjaxSource": "/News/NewsJSON",    
"bProcessing": true,    
"sPaginationType": "full_numbers",    
"iDisplayLength": 25,    
"aLengthMenu": [[25, 50, 100, 150, 200], [25, 50, 100, 150, 200]],

"aaSorting": [[1,'asc']],    
"aoColumns": [    
{ "sName": "Headline" }    
,{ "sName": "DateCreated" }    
]    
});    
});    
</script>


<table id="news" class="default-table" cellpadding="0" cellspacing="0">
<thead>

        <tr>
            <th>
                Headline
            </th>
            <th style="width: 114px">
                Created
            </th>
        </tr>
    </thead>
</table>

Or similar :) not sure if I got the js correct but.

/Lasse

UPDATE:

Bit messy but something like:

public class DataTableOptions
{
    public string ID { get; set; }
    public string Url { get; set; }
    public string Cols { get; set; }
    public bool Sort { get; set; }
    public string ViewUrlLinkname { get; set; }
    public string EditUrlLinkname { get; set; }
    public string DeleteLinkname { get; set; }
    public string DeleteTitle { get; set; }
    public string DeleteYes { get; set; }
    public string DeleteNo { get; set; }
    public string DeleteMessage { get; set; }
}

public static class DataTableHelpers
{
    private static string aLengthMenu = "[[25, 50, 100, 150, 200], [25, 50, 100, 150, 200]]";

    public static MvcHtmlString DataTable(this HtmlHelper helper, DataTableOptions options)
    {
        string[] arrcols = options.Cols.Split(',');
        int sortOffset = arrcols.Where(x => x == "Delete" || x == "View" || x == "Edit").Count();

        StringBuilder sb = new StringBuilder();

        sb.AppendLine("<script type=\"text/javascript\" charset=\"utf-8\">");
        sb.AppendLine("$(function () {");
        sb.AppendLine("$('#" + options.ID + "').dataTable({");
        sb.AppendLine("\"bSort\" : " + options.Sort.ToString().ToLower() + ",");
        sb.AppendLine("\"oLanguage\": { \"sUrl\": \"" + oLanguage + "\" },");
        sb.AppendLine("\"bServerSide\": true,");
        sb.AppendLine("\"sAjaxSource\": \"" + options.Url + "\",");
        sb.AppendLine("\"bProcessing\": true,");
        sb.AppendLine("\"sPaginationType\": \"full_numbers\",");
        sb.AppendLine("\"iDisplayLength\": 25,");
        sb.AppendLine("\"aLengthMenu\": " + aLengthMenu + ",");
        sb.AppendLine("\"aaSorting\": [[" + sortOffset.ToString() + ",'asc']],");
        sb.AppendLine("\"aoColumns\": [");


        for (int i = 0; i < arrcols.Length; i++)
        {
            if (i > 0)
                sb.Append(",");

            switch (arrcols[i])
            {
                case "Delete":
                    sb.AppendLine("{");
                    sb.AppendLine("\"sName\": \"" + arrcols[i] + "\",");
                    sb.AppendLine("\"bSearchable\": false,");
                    sb.AppendLine("\"bSortable\": false,");
                    sb.AppendLine("\"fnRender\": function (oObj) {");
                    sb.Append("return '");
                    sb.Append("<a class=\"deletelink\" href=\"' + oObj.aData["+ i.ToString() + "] + '\">" + options.DeleteLinkname + "</a> ");
                    sb.Append("';");
                    sb.AppendLine("}");
                    sb.AppendLine("}");

                    break;
                case "Edit":
                    sb.AppendLine("{");
                    sb.AppendLine("\"sName\": \"" + arrcols[i] + "\",");
                    sb.AppendLine("\"bSearchable\": false,");
                    sb.AppendLine("\"bSortable\": false,");
                    sb.AppendLine("\"fnRender\": function (oObj) {");
                    sb.Append("return '");
                    sb.Append("<a class=\"editlink\" href=\"' + oObj.aData["+ i.ToString() + "] +'\">" + options.EditUrlLinkname + "</a> ");
                    sb.Append("';");
                    sb.AppendLine("}");
                    sb.AppendLine("}");

                    break;
                case "View":
                    sb.AppendLine("{");
                    sb.AppendLine("\"sName\": \"" + arrcols[i] + "\",");
                    sb.AppendLine("\"bSearchable\": false,");
                    sb.AppendLine("\"bSortable\": false,");
                    sb.AppendLine("\"fnRender\": function (oObj) {");
                    sb.Append("return '");
                    sb.Append("<a class=\"viewlink\" href=\"' + oObj.aData["+ i.ToString() + "] + '\">" + options.ViewUrlLinkname + "</a> ");
                    sb.Append("';");
                    sb.AppendLine("}");
                    sb.AppendLine("}");

                    break;
                default:
                    sb.AppendLine("{ \"sName\": \"" + arrcols[i] + "\" }");
                    break;
            }

        }

        sb.AppendLine("]");
        sb.AppendLine("});");
        sb.AppendLine("});");
        sb.AppendLine("</script>");

        if (options.DeleteLinkname != null)
        {
            sb.Append(ConfirmHelpers.RenderConfirm(options.DeleteTitle, options.DeleteYes, options.DeleteNo, options.DeleteMessage));
        }

        return MvcHtmlString.Create(sb.ToString());
    }

and in controller pass the links as well:

    return Json(new
    {
        sEcho = param.sEcho,
        iTotalRecords = qry.TotalCount,
        iTotalDisplayRecords = qry.TotalCount,
        aaData = (
            from n in qry
            select new[]
            {
                Url.Action("Detail", new { newsID = n.NewsID, headline = n.Headline.ToURLParameter() }),
                Url.Action("Edit", new { newsID = n.NewsID, headline = n.Headline.ToURLParameter() }),
                Url.Action("Delete", new { newsID = n.NewsID }),
                n.Headline, 
                n.DateCreated.ToString() 
            }).ToArray()
    }, JsonRequestBehavior.AllowGet);

then

    @Html.DataTable(Model.DataTableOptions)

    <table id="news" class="default-table" cellpadding="0" cellspacing="0">
<thead>
        <tr>
            <th style="width: 20px">
            </th>
            <th style="width: 50px">
            </th>
            <th style="width: 40px"></th>
            <th>
                Headline
            </th>....

For delete I use plugin: http://tutorialzine.com/2010/12/better-confirm-box-jquery-css3/

public static string RenderConfirm(string title, string yes, string no, string deleteMessage)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("<script type=\"text/javascript\" charset=\"utf-8\">");
        sb.AppendLine("$(function () {");
        sb.AppendLine("$('.deletelink').live('click', function (e) {");
        sb.AppendLine("e.preventDefault();");
        sb.AppendLine("var elem = $(this);");
        sb.AppendLine("$.confirm({");
        sb.AppendLine("'title': '" + title + "',");
        //sb.AppendLine("'message': $(this).attr('data-delete-message'),");
        sb.AppendLine("'message': '" + deleteMessage + "',");
        sb.AppendLine("'buttons': {");
        sb.AppendLine("'" + yes + "': {");
        sb.AppendLine("'class': 'confirm-yes',");
        sb.AppendLine("'action': function () {");
        sb.AppendLine("$.post($(elem).attr('href'));");
        sb.AppendLine("}");
        sb.AppendLine("},");
        sb.AppendLine("'" + no + "': {");
        sb.AppendLine("'class': 'confirm-no',");
        sb.AppendLine("'action': function () { } // Nothing to do in this case. You can as well omit the action property.");
        sb.AppendLine("}");
        sb.AppendLine("}");
        sb.AppendLine("});");
        sb.AppendLine("});");
        sb.AppendLine("});");
        sb.AppendLine("</script>");

        return sb.ToString();
    }

If that's a security concern I wouldn't push everything and parse it on client side as this means no security at all. the fact that you don't render certain columns doesn't mean that data is unavailable for unauthorized users. It can be easily reached by developer tools from any browsers (Firebug) or proxies.

You need to ensure that users will get only the data they need. If you have users with different permission / access levels you need to implement RBAC - Role Based Access Control approach. Then, based on a role that particular user belongs to, you serve him with the data he needs & he is entitled to. From here you can take many paths to reach your goal. Quick and dirty way would be to create a dynamic LINQ query to select only the columns that are needed for a particular user and a simple switch/case-like structure on Role variable to construct proper query. More sophisticated would need to create column name collections for each role and filtering logic for generic LINQ result. It will be more flexible and will allow to add/remove columns easily without the need of changing the logic.

These are some high level advices/thoughts as you didn't provide any code or background on what the application is and why it needs to work like that. But again, if you're talking about security reasons for hiding data don't send it plain text to the clients browser. Someone will eventually find out that you're sending the complete set and it's only not being rendered. Using javascript for security is never good idea because ist's all on client side and can be modified in runtime.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM