# استفاده از Razor در فایل‌های JavaScript و CSS

یکی از مشکلات سینتکس Razor سمت سرور، این است که در فایل‌های JavaScript و CSS سمت کاربر نمی‌توانیم از آن استفاده کنیم، به عنوان مثال فرض کنید در یک فایل JavaScript نیاز به مشخص سازی آدرس یک اکشن متد دا

- Published: 2015-05-20
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-2080

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/2080) منتشر شده است.

<div class="postBody"><div>یکی از مشکلات سینتکس Razor سمت سرور، این است که در فایل‌های JavaScript و CSS سمت کاربر نمی‌توانیم از آن استفاده کنیم، به عنوان مثال فرض کنید در یک فایل JavaScript نیاز به مشخص سازی آدرس یک اکشن متد دارید؛ مثلاً انجام یک عملیات ای‌جکسی. در این حالت به عنوان یک Best Practice بهتر است از <a href="https://www.dntips.ir/post/891">Url.Action</a> استفاده کنید. اما همانطور که عنوان شد، این امکان یعنی استفاده از سینتکس Razor در فایل‌های JS و CSS مهیا نیست. <br/> </div> <div>ساده‌ترین راه‌حل، تولید ویوهای سمت سرور JavaScript ایی است. برای اینکار تنها کاری که باید انجام دهیم، تغییر مقدار Content-Type صفحه به مقدار موردنظر می‌باشد؛ مثلاً text/javascript برای فایل‌های JS و text/css برای فایل‌های CSS. به عنوان مثال برای فایل‌های CSS به این صورت عمل خواهیم کرد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public ActionResult Style()&#10;{&#10;            Response.ContentType = "text/css";&#10;            var model = new Style&#10;            {&#10;                Color = "red",&#10;                Background = "blue"&#10;            };&#10;            return View(model);&#10;}</pre>
 </div>
برای ویوی آن نیز خواهیم داشت:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">@model ExternalJavaScript.Models.Style&#10;@{&#10;    Layout = null;&#10;}&#10;body {&#10;    color : @Model.Color;&#10;    background-color : @Model.Background;&#10;}</pre>
 </div>
در نهایت ویوی فوق را به عنوان فایل CSS در فایل Layout استفاده خواهیم کرد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">&lt;link rel="stylesheet" href="@Url.Action("Style","Home")" /&gt;</pre>
 </div> </div> <div>برای حالت فوق می‌توانیم یک اکشن فیلتر به صورت زیر تهیه کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public class ContentType : ActionFilterAttribute&#10;    {&#10;        private string _contentType;&#10;        public ContentType(string ct)&#10;        {&#10;            this._contentType = ct;&#10;        }&#10;&#10;        public override void OnActionExecuted(ActionExecutedContext context) { /* nada */ }&#10;        public override void OnActionExecuting(ActionExecutingContext context)&#10;        {&#10;            context.HttpContext.Response.ContentType = this._contentType;&#10;        }&#10;    }</pre>
 </div>
و برای استفاده از آن خواهیم داشت:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">[ContentType("text/css")]&#10;public ActionResult Style()&#10; {&#10;     var model = new Style&#10;      {&#10;                Color = "red",&#10;                Background = "blue"&#10;      };&#10;     return View(model);&#10;}</pre>
 </div> </div> <div> <br/>
برای فایل‌های JS نیز می‌توانیم از یک View به عنوان محل قرارگیری کدهای جاوا اسکریپت استفاده کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public class JavaScriptSettingsController : Controller&#10;{&#10;        public ActionResult Index()&#10;        {&#10;            return PartialView();&#10;        }&#10;}</pre>
 </div>
در این حالت در داخل فایل Index.cshtml کدهای جاوا اسکریپت را همراه با سینتکس Razor می‌توانیم بنویسیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$(function(){&#10;    $.post('@Url.Action("GetData", "Home")', function (data) {&#10;        $('.notificationList').html(data);&#10;            if ($(data).filter("li").length != 0) {&#10;                $('#notificationCounter').html($(data).filter("li").length);&#10;            }&#10;    });&#10;});</pre>
 </div>
سپس در داخل فایل Layout.cshtml_ می‌توانیم به ویوی فوق ارجاعی داشته باشیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">&lt;script src="/JavaScriptSettings"&gt;&lt;/script&gt;</pre>
 </div> <div style="direction: rtl;">این روش به خوبی برای ویوهای JS و CSS کار خواهد کرد؛ اما از آنجائیکه ویوی ما توسط ویژوال استودیو به عنوان یک فایل JS و یا CSS معتبر شناخته نمی‌شود، Intellisense برای آن مهیا نیست. برای فعال سازی Intellisense و همچنین معتبر شناخته شدن ویوی فوق، بهترین راه‌حل قرار دادن کدهای JS درون بلاک script است (برای فایل‌های CSS نیز همینطور):</div> <div style="direction: rtl;"> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">&lt;script&gt;&#10;    $(function () {&#10;        $.post('@Url.Action("Index", "Home")', function (data) {&#10;            $('.notificationList').html(data);&#10;            if ($(data).filter("li").length != 0) {&#10;                $('#notificationCounter').html($(data).filter("li").length);&#10;            }&#10;        });&#10;    });&#10;&lt;/script&gt;</pre>
 </div>
اما با اجرای برنامه، در کنسول مرورگر بلافاصله خطای Uncaught SyntaxError: Unexpected token &lt; را دریافت خواهید کرد. در این حالت به روشی نیاز داریم که در زمان اجرا بلاک script را حذف نمائید. بنابراین از یک اکشن فیلتر سفارشی برای اینکار استفاده خواهیم کرد. کار این اکشن فیلتر، تغییر مقدار Content-Type و همچنین حذف بلاک مورد نظر می‌باشد:</div> <div style="direction: rtl;"> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">public class ExternalFileAttribute : ActionFilterAttribute&#10;    {&#10;        private readonly string _contentType;&#10;        private readonly string _tag;&#10;        public ExternalFileAttribute(string ct, string tag)&#10;        {&#10;            this._contentType = ct;&#10;            _tag = tag;&#10;        }&#10;&#10;        public override void OnResultExecuted(ResultExecutedContext filterContext)&#10;        {&#10;            var response = filterContext.HttpContext.Response;&#10;            response.Filter = new StripEnclosingTagsFilter(response.Filter, _tag);&#10;            response.ContentType = _contentType;&#10;        }&#10;&#10;        private class StripEnclosingTagsFilter : MemoryStream&#10;        {&#10;            private static Regex _leadingOpeningScriptTag;&#10;            private static Regex _trailingClosingScriptTag;&#10;&#10;            //private static string Tag;&#10;&#10;            private readonly StringBuilder _output;&#10;            private readonly Stream _responseStream;&#10;&#10;            /*static StripEnclosingTagsFilter()&#10;            {&#10;                LeadingOpeningScriptTag = new Regex(string.Format(@"^\s*&lt;{0}[^&gt;]*&gt;", Tag), RegexOptions.Compiled);&#10;                TrailingClosingScriptTag = new Regex(string.Format(@"&lt;/{0}&gt;\s*$", Tag), RegexOptions.Compiled);&#10;            }*/&#10;&#10;            public StripEnclosingTagsFilter(Stream responseStream, string tag)&#10;            {&#10;                _leadingOpeningScriptTag = new Regex(string.Format(@"^\s*&lt;{0}[^&gt;]*&gt;", tag), RegexOptions.Compiled);&#10;                _trailingClosingScriptTag = new Regex(string.Format(@"&lt;/{0}&gt;\s*$", tag), RegexOptions.Compiled);&#10;&#10;                _responseStream = responseStream;&#10;                _output = new StringBuilder();&#10;            }&#10;&#10;            public override void Write(byte[] buffer, int offset, int count)&#10;            {&#10;                string response = GetStringResponse(buffer, offset, count);&#10;                _output.Append(response);&#10;            }&#10;&#10;            public override void Flush()&#10;            {&#10;                string response = _output.ToString();&#10;&#10;                if (_leadingOpeningScriptTag.IsMatch(response) &amp;&amp; _trailingClosingScriptTag.IsMatch(response))&#10;                {&#10;                    response = _leadingOpeningScriptTag.Replace(response, string.Empty);&#10;                    response = _trailingClosingScriptTag.Replace(response, string.Empty);&#10;                }&#10;&#10;                WriteStringResponse(response);&#10;                _output.Clear();&#10;            }&#10;&#10;            private static string GetStringResponse(byte[] buffer, int offset, int count)&#10;            {&#10;                byte[] responseData = new byte[count];&#10;                Buffer.BlockCopy(buffer, offset, responseData, 0, count);&#10;&#10;                return Encoding.Default.GetString(responseData);&#10;            }&#10;&#10;            private void WriteStringResponse(string response)&#10;            {&#10;                byte[] outdata = Encoding.Default.GetBytes(response);&#10;                _responseStream.Write(outdata, 0, outdata.GetLength(0));&#10;            }&#10;        }&#10;    }</pre>
 </div> </div> <div style="direction: rtl;"> در نهایت می‌توانیم اکشن‌متد موردنظرمان را با فیلتر سفارشی مزین کنیم:</div> <div style="direction: rtl;"> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">[ExternalFile("text/javascript", "script")]&#10;public ActionResult Index()&#10;{&#10;      return PartialView();&#10;}</pre>
 </div>
برای تولید ویوهای CSS نیز کافی است مقادیر فیلتر را تغییر دهیم:</div> <div style="direction: rtl;"> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">[ExternalFile("text/css", "style")]&#10;public ActionResult Style()&#10;{&#10;            var model = new Style&#10;            {&#10;                Color = "red",&#10;                Background = "blue"&#10;            };&#10;            return View(model);&#10;}</pre>
 </div> </div> </div></div>
