2019/03/12

ASP.Net Core Razor ajax get result

這篇廢文是用於練功並記錄下來,實作線上編譯Java並輸出結果
透過Process的方式去掉用java及javac,並且將結果透過async方式取得避免線程賭塞
關於CSRF的介紹可以參考這篇「讓我們來談談 CSRF

Startup.cs
這邊要在ConfigureServices加入
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

要設定這個主要是因為XSRF/CSRF



using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace WebOnlineEditorApplication
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }
            
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseMvc();
        }
    }
}







Index.cshtml
前端部分主要如下的程式碼
不過做ajax時需要加入,不然會有XSRF/CSRF導致無法呼叫



beforeSend: function (xhr) {
    xhr.setRequestHeader("XSRF-TOKEN",
    $('input:hidden[name="__RequestVerificationToken"]').val());
},


@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        $('#btn_run').click(function (e) {
            $code = $('#code').val();
            $.ajax({
                url: '/?handler=Result',
                type: 'GET',
                data: {
                    code:$code
                },
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("XSRF-TOKEN",
                        $('input:hidden[name="__RequestVerificationToken"]').val());
                },
                dataType:"json"
            }).done(function (data) {
                $('#result').val(data);
            });
            

        });
    });


</script>

<div>
    <textarea id="code" rows="10" cols="50"></textarea><br />
    <button id="btn_run">Run</button><br />
    <textarea id="result" rows="10" cols="50"></textarea><br />
</div>

後端程式碼則需要再該呼叫的方法上加上

[ValidateAntiForgeryToken]

Index.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace WebOnlineEditorApplication.Pages
{
    public class IndexModel : PageModel
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly static String COMPILER_PATH = @"C:\Program Files\Java\jdk-11.0.2\bin\javac";
        /// <summary>
        /// 
        /// </summary>
        private readonly static String RUN_PATH = @"C:\Program Files\Java\jdk-11.0.2\bin\java";
        /// <summary>
        /// 
        /// </summary>
        private readonly static String JAVA = "{0}.java";


        /// <summary>
        /// Get Result
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [ValidateAntiForgeryToken]
        public async Task<JsonResult> OnGetResult(String code)
        {
            JsonResult result = null;
            String className = code.Split(Environment.NewLine)[0].Substring(code.IndexOf("s") + 2, code.IndexOf('{') - code.IndexOf("s") - 2).Replace(" ", "");
            String path = Directory.GetCurrentDirectory() + @"\";


            if (code != null && !code.Equals(String.Empty))
            {
                using (FileStream stream = new FileStream(String.Format(JAVA, className), FileMode.OpenOrCreate, FileAccess.ReadWrite))
                {
                    byte[] b = Encoding.UTF8.GetBytes(code);
                    await stream.WriteAsync(b, 0, b.Length);
                    stream.Flush();
                }
            }

            String resultMsg = String.Empty;
            String errMsg = String.Empty;
            using (Process process = new Process())
            {
                process.StartInfo.FileName = COMPILER_PATH;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardInput = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.FileName = COMPILER_PATH;
                process.StartInfo.Arguments = String.Format(JAVA, path + className);
                process.Start();
                resultMsg = await process.StandardOutput.ReadToEndAsync();
                errMsg = await process.StandardError.ReadToEndAsync();
                process.WaitForExit();
                Debug.WriteLine(errMsg);
            }

            if (System.IO.File.Exists(String.Format(JAVA, className)))
                System.IO.File.Delete(String.Format(JAVA, className));

            if (!errMsg.Equals(String.Empty))
            {
                result = new JsonResult(errMsg);
                return result;
            }
            else
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = RUN_PATH;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.RedirectStandardInput = true;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.RedirectStandardError = true;
                    process.StartInfo.FileName = RUN_PATH;
                    process.StartInfo.Arguments = className;
                    process.StartInfo.WorkingDirectory = path;
                    process.Start();
                    resultMsg = await process.StandardOutput.ReadToEndAsync();
                    errMsg = await process.StandardError.ReadToEndAsync();
                    process.WaitForExit();
                }
                if (errMsg.Equals(String.Empty))
                {
                    result = new JsonResult(errMsg);
                }
                result = new JsonResult(resultMsg);
            }

            if (System.IO.File.Exists(String.Format("{0}.class", className)))
                System.IO.File.Delete(String.Format("{0}.class", className));

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        public void OnGet()
        {
        }
    }
}

執行結果:




參考資料:
https://docs.microsoft.com/zh-tw/aspnet/core/security/anti-request-forgery?view=aspnetcore-2.2
https://blog.techbridge.cc/2017/02/25/csrf-introduction/