Custom MessageHandler for Request/Resposne Processing
/// <summary>
/// Intercepts incoming requests and outgoing responses for custom processing.
/// </summary>
public class RequestResponseInterceptor : MessageProcessingHandler
{
protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
{
var contentType = request.Content.Headers.ContentType;
var data = System.Web.HttpContext.Current.Request["data"];
if (string.IsNullOrWhiteSpace(data))
return request;
try
{
if (request.Method == HttpMethod.Post)
{
request.Content = new StringContent(data, Encoding.UTF8, "application/json");
var routeData = request.GetRouteData();
routeData.Values.Add("signdata", data);
}
else
{
var parameters = Newtonsoft.Json.JsonConvert.DeserializeObject<IDictionary<string, string>>(data);
var queryString = string.Join("&", parameters.Select(kv => $"{kv.Key}={kv.Value}"));
var baseUri = request.RequestUri.AbsoluteUri.Split('?')[0];
request.RequestUri = new Uri($"{baseUri}?{queryString}");
request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
}
}
catch (Exception ex)
{
request.Content = new StringContent("", Encoding.UTF8, "application/json");
request.CreateResponse(HttpStatusCode.Gone, "Exception");
return request;
}
return request;
}
protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
{
if (response.StatusCode != HttpStatusCode.OK)
return response;
var resultTask = response.Content.ReadAsStringAsync();
resultTask.Wait();
var resultData = Newtonsoft.Json.JsonConvert.DeserializeObject<ResultData<object>>(resultTask.Result);
if (resultData != null && resultData.Code == 200)
{
var dateFormatter = new Newtonsoft.Json.Converters.IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
var serializedData = Newtonsoft.Json.JsonConvert.SerializeObject(resultData.Data, Newtonsoft.Json.Formatting.None, dateFormatter);
resultData.Data = serializedData;
response.Content = new ObjectContent<ResultData<object>>(resultData, new System.Net.Http.Formatting.JsonMediaTypeFormatter());
}
return response;
}
}
Attribute to Bypass Signature Verification
/// <summary>
/// Indicates that a controller or action should skip signature validation.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SkipSignCheck : Attribute { }
Action Filter for Signature Verification
/// <summary>
/// Validates the 'sign' parameter against the stored 'signdata' from the route.
/// </summary>
public class SignValidationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// Check for skip attribute at controller or action level
if (actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<SkipSignCheck>().Any() ||
actionContext.ActionDescriptor.GetCustomAttributes<SkipSignCheck>().Any())
{
return;
}
var httpRequest = HttpContext.Current.Request;
var signValue = httpRequest["sign"];
var storedSignData = actionContext.Request.GetRouteData().Values["signdata"] as string;
if (string.IsNullOrWhiteSpace(storedSignData) || !string.Equals(storedSignData, signValue, StringComparison.OrdinalIgnoreCase))
{
actionContext.Response = CreateSignFailureResponse();
}
}
private HttpResponseMessage CreateSignFailureResponse()
{
var failureResult = new ResultData<object>
{
Code = 403,
Msg = "SignError"
};
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ObjectContent(failureResult.GetType(), failureResult, new JsonMediaTypeFormatter())
};
}
}
These components allow you to intercept HTTP requests and responses globally via MessageProcessingHandler, and apply per-action/controller signatrue verification using an action filter combined with a custom attribute.