Skip to content

Instantly share code, notes, and snippets.

@relyky
Created April 8, 2022 02:39
Show Gist options
  • Save relyky/3ed82b3296417ca10ffbe532a9c8e1e7 to your computer and use it in GitHub Desktop.
Save relyky/3ed82b3296417ca10ffbe532a9c8e1e7 to your computer and use it in GitHub Desktop.
MVC5, AjaxWebApi, AjaxValidateAntiForgeryToken, AntiForgery, with axios
using System;
using System.Net;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using YourProject.DB;
using YourProject.Models;
namespace YourProject
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AjaxValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
try
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
ValidateRequestHeader(filterContext.HttpContext.Request);
}
else
{
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
catch (HttpAntiForgeryException ex)
{
throw new HttpAntiForgeryException("The anti-forgery token not found", ex);
}
}
private void ValidateRequestHeader(HttpRequestBase request)
{
string antiForgeryToken = request.Headers["RequestVerificationToken"];
VerifyAntiForgeryToken(antiForgeryToken);
}
private void VerifyAntiForgeryToken(string antiForgeryToken)
{
string cookieToken = String.Empty;
string formToken = String.Empty;
if (!String.IsNullOrEmpty(antiForgeryToken))
{
string[] tokens = antiForgeryToken.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
}
}
using System;
using System.Net;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using YourProject.DB;
using YourProject.Models;
namespace YourProject
{
/// <summary>
/// HttpPost + AjaxValidateAntiForgeryToken + CatchAndLog
/// </summary>
public class AjaxWebApiAttribute : AjaxValidateAntiForgeryTokenAttribute, IExceptionFilter, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
}
/// <summary>
/// 實作[HttpPost]
/// </summary>
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.HttpMethod != "POST")
{
filterContext.Result = new HttpNotFoundResult();
}
}
/// <summary>
/// 實作[CatchAndLog]
/// </summary>
public void OnException(ExceptionContext filterContext)
{
//System.Diagnostics.Debugger.Break();
string errMsg = filterContext.Exception.Message;
// 記LOG
string traceMsg = $"[{filterContext.Exception.GetType().Name}] {errMsg} \r\nRawUrl:{filterContext.HttpContext.Request.RawUrl}";
DBHelper.ExpLog("AjaxWebApi", "CatchAndLog", traceMsg, SysEnv.AuthUser?.UserId ?? "");
// response 299 表示邏輯上的錯誤
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = 299;
filterContext.Result = new ContentResult
{
Content = errMsg
};
}
}
}
/// ref→[axios - Config Defaults](https://github.com/axios/axios#config-defaults)
//axios.defaults.headers.common['Authorization'] = 'Bearer ' + AuthToken;
axios.defaults.headers.common['RequestVerificationToken'] = AntiForgeryToken;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // mark as an ajax request
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.validateStatus = (status) => (status >= 200 && status < 299); // 客製化讓 StatusCode 299 表示為邏輯上的錯誤訊息。
///---------------------------------------------------------------
/// CommonUtilities.js
/// 解析 Response 封包,並 alert 錯誤訊息
export function castAlertPromise(originalPromise) {
return Promise.resolve(originalPromise).then(resp => {
//# success
console.info('castAlertPromise', { resp });
return resp.data;
}).catch(xhr => {
//# fail
console.error('castAlertPromise FAIL1', { xhr })
const errmsg = xhr.response.data
Swal.fire({ title: '執行失敗!', text: errmsg, icon: 'error' });
throw errmsg; //※必需丟出錯誤訊息才會被browser視為執行失敗!
});
}
///---------------------------------------------------------------
/// apiClient.js
import axios from 'axios'
import { castAlertPromise } from './CommonUtilities.js'
const baseUrl = '/api/Controller/Action'
export default {
QryDataList: (args) => {
const url = `${baseUrl}/QryDataList`
return castAlertPromise(axios.post(url, args))
},
GetFormData: (args) => {
const url = `${baseUrl}/GetFormData`
return castAlertPromise(axios.post(url, args))
},
NewFormData: (args) => {
const url = `${baseUrl}/NewFormData`
return castAlertPromise(axios.post(url, args))
},
UpdFormData: (args) => {
const url = `${baseUrl}/UpdFormData`
return castAlertPromise(axios.post(url, args))
},
DelFormData: (args) => {
const url = `${baseUrl}/DelFormData`
return castAlertPromise(axios.post(url, args))
},
}
@functions {
public static string GetAntiForgeryToken()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return string.Concat(cookieToken, ":", formToken);
}
public static string CalcFileHashCode(string filepath)
{
using (var stream = File.OpenRead(Server.MapPath(filepath)))
using (var bufferedStream = new BufferedStream(stream, 1024 * 32))
{
var sha = new System.Security.Cryptography.SHA256Managed();
byte[] checksum = sha.ComputeHash(bufferedStream);
return BitConverter.ToString(checksum).Replace("-", String.Empty);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment