ASP.NET MVC 3 使用CKEditor

转载http://kevintsengtw.blogspot.com/2011/12/aspnet-mvc-3-ckeditor.html,由于某些原因国内可能打不开特此转载


以往在开发ASP.NET WebForm 专案时,如果专案需求是有编辑HTML 图文资料时,大多都会选择使用FCKEditor,FCKEditor 是个相当不错的HTML 编辑器,能够符合许多情境的需求,然而FCKEditor 的开发已经停止,转而开发CKEditor,CKEditor 是将原本的FCKEditor 给全部重新写过,虽然大致上的设定还是与原本的FCKEditor 相去不远,但却有着很大的不同,我在ASP.NET MVC 3 专案上还没有对CKEditor 做过整合,所以此次就练习在ASP.NET MVC 3 专案上整合CKEditor 的功能,使用NuGet 来取得CKEditor,并且试着用不同的方式在前端与后端的传值上做些不同的变化。



CKEditor

目前版本: 3.6.2

网址: http://ckeditor.com/

image

Demo: http://ckeditor.com/demo

Download: http://ckeditor.com/download

Documention: http://docs.cksource.com/

接下来这个连结是我认为比较重要的,很多的设定与CKEditor的重要资讯都可以在里面找到,

CKEditor 3 JavaScript API Documentation

http://docs.cksource.com/ckeditor_api/index.html

在Download的最下方还是有提供FCKEditor最后版本的下载,不过还特别注明:retired (退休啦~)

image


一开始我们就说过,我们要在Visual Studio中使用NuGet来安装CkEd​​itor,所以就先别急着在官网上面去把最新的版本给下载下来,以下是CKEditor在NuGet Gallery的相关资讯。

NuGet Gallery - CKEditor 3.6.2

http://nuget.org/packages/CKEditor

PM> Install-Package CKEditor

image


于新的ASP.NET MVC 3网站中加入CKEditor

直接再VS2010中新增一个空白的ASP.NET MVC3网站专案

SNAGHTML7ffa48

选择Razor的ViewEngine(如果不熟悉的话也可以切换为ASPX的ViewEngine)

SNAGHTML809973

按下确定之后就会建立好一个「空空」的网站内容,真的是空的…

image

这时候还不用急着去建立新的Controller、View等,要先做的就是开启NuGet Manage对专案中的套件、组件、函式库做升级的动作。

SNAGHTML871325

上述的升级动作都做完之后,接下来就是切换到Manage NuGet Package视窗的「Online」并且在搜寻列输入「CKEditor」

SNAGHTML8c0408

在上面的所搜寻出来的CKEditor中按下「Install」就可以将CKEditor完整安装到网站中,安装的过程需要一段时间,因为会把所有CKEditor会用到的JS档案、图档等都一一下载到网站专案里。

SNAGHTML8dd461

安装完成后,会在Scripts目录中看到完成安装的CKEditor

image


使用CKEditor

安装好CKEditor 之后就是要来使用它啦…至于中间过程的建立Controller 与ViewPage 的步骤就跳过,直接进入到使用的步骤来说明。

首先比较重要的是,先在Views/Shared目录下的_Layout.cshtml去加入「 Scripts/ckeditor/ckeditor.js 」,像我的话,我都会把Include Javascript的部份给抽出来另外建立为一个部分检视,然后也会在_Layoout.cshtml 去加入一个RenderSection,用来放置每个ViewPage所要建立的Javascript程式,

如下:

Views/Shared/_Layout.cshtml

  < !DOCTYPE html >
  < html >
  < head >
  < title > @ViewBag.Title </ title >
  < link href = "@Url.Content(" ~/ Content / Site . css ")" rel = "stylesheet" type = "text/css" />
  </ head >
  < body >
  @RenderBody()
  @Html.Partial("IncludeJavascript")
  @RenderSection("JavascriptContent", required: true)
  </ body >
  </ html > 

Views/Shared/IncludeJavascript.cshtml

  < script src = "@Url.Content(" ~/ Scripts / jquery - 1 . 7 . 1 . min . js ")" type = "text/javascript" > </ script >
  < script src = "@Url.Content(" ~/ Scripts / ckeditor / ckeditor . js ")" type = "text/javascript" > </ script > 

接着下来,建立一个Craeate.cshtml,这个ViewPage是呈现一个简单的文章编辑页面,而文章内容就是要使用CKEditor

  @model Test.Web.ODP.Models.Article
  @{
  ViewBag.Title = "Create";
  Layout = "~/Views/Shared/_Layout.cshtml";
  }
  < h2 > Create </ h2 >
  @using (Html​​.BeginForm("Create", "Home", FormMethod.Post, new { id = "FormCreate" }))
  {
  < fieldset >
  < legend > Article </ legend >
  < div class = "editor-label" >
  @Html.LabelFor(model => model.SUBJECT)
  </ div >
  < div class = "editor-field" >
  @Html.TextBox("subject", null, new { id = "subject", style = "width: 400px", MaxLength = "100" })
  </ div >
  < div class = "editor-label" >
  @Html.LabelFor(model => model.CONTENT)
  </ div >
  < div class = "editor-field" >
  @Html.TextArea("content", new { id = "content", @name = "content" })
  </ div >
  < p >
  < input type = "button" value = "Create" id = "ButtonCreate" />
  </ p >
  </ fieldset >
   
  }
  < div >
  @Html.ActionLink("Back to List", "Index")
  </ div > 

其中「 @Html.TextArea("content", new { id = "content", @name = "content" }) 」建立一个TextArea,而这个TextArea就是CKEditor要取代的Html Element,

再来就是要在Javascript的区域中去做宣告,让这个TexrArea置换为CKEditor

  < script type = "text/javascript" language = "javascript" >
  var editor = CKEDITOR.editor.replace('content', { skin: 'kama', width: '800px' });
  </ script > 

CKEDITOR.editor.replace()方法

第一个参数就是要指定取代的TextArea Name,

第二个参数则是设定这个CKEditor的外观样式,如外观样式、高度、宽度等,

有关skin 的外观样式,可以开启Scripts/ckeditor/skins 去找到外观样式的名称,安装后预设有Kama, office2003, v2

image

设定好之后,就可以来看看这个页面的执行结果,

image


把CKEditor里面的内容传送到Server端

在CKEditor 所编辑好的内容当然还是要送回到Server端去做储存的处理,其中最简单的方式就是直接用Form Post传送,要用Form Post 传送到Server端,当然就是要先在ViewPage中去usgin Hrml.BeginForm,如下:

image

而这个Form所要使用的Action网址就是Controller中的Create Action方法,在ASP.NET WebForm开发,当遇到前端要传送含有HTML Tag的内容时,如果没有在该页面去设定ValidateInput = false,那一定会出现警告讯息,同理,在ASP.NET MVC也是一样,但是ValidateInput的设定就不是在ViewPage上面去做设定,而且可以去设定的地方有两个地方。

先来看看如果不去设定ValidateInput时会看到的画面:

image

设定的地方一:

Controller 中所对应的Action方法上,使用Attribute「ValudateInput」,除了使用HttpPost之外,要设定ValidateInput(false)

image

而在这边一定要特别的强调,因为我们将ValidateInput给设定为false,无疑就是开了一道安全大门给有心破坏的人来擅闯,所以为了避免网站遭受到XSS攻击或是输入的内容去带有不友善或是恶意的指令码,这个时候我们一定要再加上一道安全的锁,

Microsoft Web Protection Library – AntiXSS Library V4.0

Codeplex: http://wpl.codeplex.com/

Download: ww.microsoft.com/download/en/details.aspx?id=5242

NuGet: https://nuget.org/packages/AntiXSS

于VS2010就可以透过NuGet帮网站专案安装上AntiXSS

SNAGHTMLe6d388

相关介绍与使用方法,请各位参阅网路上的文件与资料。

程式中使用AntiXSS的Sanitizer.GetSageHtmlFragement() 方法,取得安全的HTML区段内容。

image

在Controller的Create Action方法加上[Validate(false)] Attribute后就可以前端传送带有HTML Tag内容的资料道Server端,请记得一定要让网站加上AntiXSS并且使用,以加强防护。

设定的地方二:

除了 ​​在Action方法加上[Validate(false)] Attribute外,另外也还有个地方可以设定,在Scripts/ckeditor目录下找个档案「 config.js

档案内容:

image

config.js可以对CKEditor做很多的设定,不过这些并不在此次的说明范围中,在这个config.js档案中可以加上「config.htmlEncodeOutput = true; 」,修改如下:

image

修改好config.js 后,为了验证这里的修改,我们把原本加在HomeController Create Action方法前的Attribute拿掉,

image

接着就执行程式来看看执行的状况:

不会在出现黄页的警告讯息,而且有可以顺利执行Action方法

image

所以可以确定在ckeditor/config.js加上config.htmlEncodeOutput = true的状况下,Controller的Action方法是可以不用加上[Validate(false)] Attribute ,不过咧~最好的作法还是不论前后端,都各自加上设定,也加上AntiXSS的防护。

回到传送资料到后端的部份,前面说到最直接的方式就是用Form Post的方式,但有时会为了不要让整页做POST的动作而想要用AJAX的方法将锁需要传送到后端的值给POST出去,这时候就必须要在前端先把FCKEditor的内容给取出来,再使用jQuery的AJAX方法然后与其他资料一并送到Server端。

前端使用jQuery AJAX方法传送资料到Server端- 1

前端的Javascript的程式中要去取得CKEditor的内容,不能直接以下的方法来取得:

  var content = $('#content').val(); 

这样是取不到内容的,因为TextArea content已经在之前用以下的程式给置换为CKEditor:

  var editor = CKEDITOR.editor.replace('content', { skin: 'kama', width: '800px' }); 

所以应该是需要用下列的方式来取得CKEditor的内容:

  var content = editor.getData(); 

那么前端的完整程式如下:

  function CreateArticle()
  {
  var subject = $.trim($('#subject').val());
  var content = editor.getData();
  if (subject. length == 0)
  {
  alert ('请输入标题');
  return false ;
  }
  if (content. length == 0)
  {
  alert ('请输入内容');
  return false ;
  }
  $.ajax(
  {
  url: '@Url.Action(" Create ", " Home ")',
  type: 'post',
  data: { subject: subject, content: content },
  cache: false ,
  async: false ,
  dataType: 'json',
  success: function (data)
  {
  if (data.Msg)
  {
  alert (data.Msg);
  return false ;
  }
  else
  {
  if (data.Result == 'Success')
  {
  alert ('Success');
  location .href = '@Url.Action(" Index ", " Home ")';
  }
  else
  {
  alert (data.ResultMessage);
  return false ;
  }
  }
  }
  });
  } 

透过上面的程式处理就可以把指定的资料给传送到Server端,不过这样的处理方式会比较适合网页表单的栏位比较少的情况下,哪万一栏位变得很多,用一个一个取值然后放值的动作就会显得很费力也相当不聪明,不想透过Form-post的方式又要处理很多个表单栏位资料时,jQuery还有另外两个方法,一个是serialize() 另一个是serializeArray ().

前端使用jQuery AJAX方法传送资料到Server端- 2 使用serialize()

先来看看使用使用serialize() 来​​取得FormCreate表单下的栏位资料内容,

  var data = $('#FormCreate').serialize();
  console.log(data); 

取得的资料:

image

可以看到content是取不到资料的,此时可以用以下的取值方式取得资料后再把资料给加入到serialize()的资料中,

  var content = editor.getData(); 

不过$('#FormCreate').serialize(); 所取得的资料是字串,要再将CKEditor的内容给串接到一个字串中,并不是很理想。

前端使用jQuery AJAX方法传送资料到Server端- 3 使用serializeArray()

以serializeArray()所取得表单资料:

image

使用serializeArray()所取得的资料会以Name-Value的方式处理为物件阵列,如此一来会比直接用字串的串接处理更好,因为是阵列的资料,我们可以使用我之前所介绍的Linq to Javascript (JSLINQ)来处理这个阵列,并且把editor.getData()的资料给放到阵列中name = “content”的物件value中,如下:

  var values ​​= $(" #FormCreate ").serializeArray();
  new JSLINQ(values).Where( function (item)
  {
  if (item[" name "] == " content " && item[" value "]. length == 0)
  {
  item[" value "] = editor.getData();
  }
  });

最后在jQuery.AJAX方法中,data还是要以字串的方式传送出去,这个时候可以使用jQuery.Param()方法来处理阵列的资料,

  jQuery.param(values​​), 

以下是完整的前端处理程式:

  function CreateArticle()
  {
  var subject = $.trim($('#subject').val());
  var content = editor.getData();
  if (subject. length == 0)
  {
  alert ('请输入标题');
  return false ;
  }
  if (content. length == 0)
  {
  alert ('请输入内容');
  return false ;
  }
  var values ​​= $(" #FormCreate ").serializeArray();
  new JSLINQ(values).Where( function (item)
  {
  if (item[" name "] == " content " && item[" value "]. length == 0)
  {
  item[" value "] = editor.getData();
  }
  });
  $.ajax(
  {
  url: '@Url.Action(" Create ", " Home ")',
  type: 'post',
  data: jQuery.param(values​​),
  cache: false ,
  async: false ,
  dataType: 'json',
  success: function (data)
  {
  if (data.Msg)
  {
  alert (data.Msg);
  return false ;
  }
  else
  {
  if (data.Result == 'Success')
  {
  alert ('Success');
  location .href = '@Url.Action(" Index ", " Home ")';
  }
  else
  {
  alert (data.ResultMessage);
  return false ;
  }
  }
  }
  });
  } 

另外也提供Server端的HomeController Create() Action程式内容:

  [HttpPost]
  [ValidateInput( false )]
  public ActionResult Create( string subject, string content)
  {
  Dictionary< string , string > jo = new Dictionary< string , string >();
  
  if ( string .IsNullOrWhiteSpace(subject))
  {
  jo.Add(" Msg ", " 没有输入标题 ");
  return Content(JsonConvert.SerializeObject(jo), " application/json ");
  }
  if ( string .IsNullOrWhiteSpace(content))
  {
  jo.Add(" Msg ", " 没有输入内容 ");
  return Content(JsonConvert.SerializeObject(jo), " application/json ");
  }
  try
  {
  Article article = new Article();
  article.SUBJECT = Sanitizer.GetSafeHtmlFragment(subject);
  article.CONTENT = Sanitizer.GetSafeHtmlFragment(content);
  articleService.Create(article);
  jo.Add(" Result ", " Success ");
  }
  catch (Exception ex)
  {
  jo.Add(" Result ", " Failure ");
  jo.Add(" ResultMessage ", ex.Message);
  }
  return Content(JsonConvert.SerializeObject(jo), " application/json ");
  }
  public ActionResult Details( string id)
  {
  if ( string .IsNullOrWhiteSpace(id))
  {
  return RedirectToAction(" Index ", " Home ");
  }
  Article article = articleService.FindOne(id);
  return View(article);
  }
  public ActionResult Edit( string id)
  {
  return View();
  }
  } 

参考连结:
demoshop - ASP.NET MVC validateRequest取消验证失效?
KKBruce - ASP.NET MVC - HTML编辑器| (3) CKEditor + CKFinder
CKEditor 3 JavaScript API Documentation
Class CKEDITOR.editor - getData()
Namespace CKEDITOR.config - CKEDITOR.config.htmlEncodeOutput
jQuery
. serialize()
.serializeArray()
jQuery.param()

以上

添加评论

  Country flag

biuquote
  • 评论
  • 在线预览
Loading

Calendar

<<  十一月 2017  >>
星期星期星期星期星期星期星期
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

在日历中浏览文章

Month List