본문 바로가기

C#/근웹 연대기

c#으로 근본 없는 웹서버 개발기 27 : cshtml 클래스 파일 만들기

⚠WARNING
ASP.NET에 대한 포스팅이 아닙니다
나가실 문은 오른쪽 하단입니다

 

 

점점 대단원 하나가 마무리되고 있는 느낌이다.

이전 글의 내용을 토대로 이번에는 html을 만드는 클래스 파일을 만든다.

View클래스를 상속 받음으로, 그전에 View 클래스를 다음과 같이 작성하였다.

namespace dotweb;
public abstract class View
{
    protected View sub = null;
    protected Request req;
    protected Response res;
    protected TempJO TempData;
    protected JO ViewData;
    protected dynamic ViewBag;


    protected String Layout = null;
    private StringBuilder html = new();
    private readonly Stack<string> secStack = new(new[] { "@Body" });
    private readonly Dictionary<string, StringBuilder> section;

    public View(View sub) : this(sub.res, sub.ViewData, (object)sub.ViewBag, sub.TempData)
    {
        this.sub = sub;
    }

    public View(Response res, JO ViewData, dynamic ViewBag, TempJO TempData)
    {
        this.req = res?.req;
        this.res = res;
        this.ViewData = ViewData ?? new JO();
        this.ViewBag = ViewBag ?? new System.Dynamic.ExpandoObject();
        this.TempData = TempData ?? new TempJO();
        this.section = new() { [secStack.Peek()] = html };
    }

    public abstract string GetHTML();
    protected void W(object str) => html.AddL("" + str);
    protected string GetW() => html.ToString().Replace('"', '"');

    protected string RenderBody() => RenderSection(secStack.First());
    protected string RenderSection(string name, bool required = false)
    {
        if (sub.section.ContainsKey(name) == false)
        {
            if (required)
            {
                ArgumentException ex = new("there is no Section", nameof(name));
                throw ex;
            }
            return "";
        }
        return sub.section[name].ToString();

    }
    protected void SetSection(string name)
    {
        secStack.Push(name);
        if (section.ContainsKey(name)) html = section[name];
        else html = section[name] = new StringBuilder();

    }
    protected void OffSection(string name)
    {
        if (secStack.Pop() != name)
        {
            ArgumentException ex = new("Not match", nameof(name));
            throw ex;
        }

        html = section[secStack.Peek()];
    }

    protected string GetClassName() => GetClassName(Layout);
    public static string GetClassName(string Layout)
    {
        var name = Layout.Trim('"').Trim('~');
        name = name.Replace('/', '_').Replace('\\', '_');
        name = name[0..name.LastIndexOf('.')];
        return name;
    }
}

본인이 임으로 만든 클래스가 여럿 있으므로 과거의 글을 보지 않으면 살짝 갸우뚱할 수 있다.

 

그럼 다음과 같이 Write 할 파일의 내용을 만든다.

namespace dotweb;
public partial class TemplateEngine
{
    StringBuilder sb_using, sb_func;
    String layout;

    private string Node2ViewClass(String name, ParseNode node)
    {
        (sb_using, sb_func, layout) = (new(), new(), null);
        var body = Node2String(node);

        StringBuilder sb = new();
        sb.AddL("using " + this.GetType().Namespace + ";");
        sb.AddL(sb_using.ToString());
        sb.AddL("namespace Views;");
        sb.AddL("//--------------------------------------------");
        sb.AddL("// <auto-generated>                           ");
        sb.AddL("//     This code was generated by a tool.     ");
        sb.AddL("// </auto-generated>                          ");
        sb.AddL("//--------------------------------------------");
        sb.AddL("public class " + name + " : View");
        sb.AddL("{");
        sb.AddL("    public " + name + "(Response res, JO ViewData, dynamic ViewBag, TempJO TempData) :");
        sb.AddL("        base(res, ViewData, (object)ViewBag, TempData) { }");
        sb.AddL("    public " + name + "(View child) : base(child) { }");
        sb.AddL(sb_func.ToString());
        sb.AddL("    public override string GetHTML()");
        sb.AddL("    {");
        sb.AddL("        Layout = " + (layout ?? "null") + ";");
        sb.AddL(body);
        sb.AddL("        if (Layout == null) return GetW();");
        sb.AddL("        var type = Type.GetType(\"Views.\" + GetClassName());");
        sb.AddL("        return (Activator.CreateInstance(type, this) as View).GetHTML();");
        sb.AddL("    }");
        sb.AddL("}");


        return sb.ToString(); ;
    }
}

이곳에는 기입하지 않았지만 Node2String은 이전 글에서 작성한 함수로서 같은 클래스 내에 위치하므로 sb_using, sb_func, layout 에 각각 접근하여 각각 갱신한다.

 

다음 포스팅에는 이것을 파일로 저장하고 빌드하는 과정을 다룰 예정.

 

<부록>

TempJO.cs

namespace dotweb;
public class TempJO : JO
{
    private readonly HashSet<string> check = new();
    private bool keepAll = false;

    public void Keep(string key = null)
    {
        if (key == null)
        {
            keepAll = true;
            return;
        }
        if (Has(key)) check.Add(key);
    }

    internal void Commit()
    {
        if (keepAll)
        {
            keepAll = false;
            return;
        }
        foreach (string key in Keys)
        {
            if (!check.Contains(key)) Remove(key);
        }
        check.Clear();
        check.Clear();
    }
    public object Peek(string key){
        Keep(key);
        return base.GetValue(key);
    }
}