Implementing Custom MessageHandler and Sign Filter in ASP.NET Web API

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.

Tags: ASP.NET Web API MessageHandler ActionFilter Signature Validation Custom Attribute

Posted on Sat, 09 May 2026 18:20:28 +0000 by Stuph