Search code examples
asp.net-web-apihyprlinkr

How to generate a link to an HTTP POST action with Hyprlinkr?


I'm trying to use Hyprlinkr to generate URL to the HTTP Post action. My controller looks like this:

public class MyController : ApiController {
    [HttpPost]
    public void DoSomething([FromBody]SomeDto someDto) {
        ...
    }
}

with this route:

routes.MapHttpRoute(
            name: "MyRoute",
            routeTemplate: "dosomething",
            defaults: new { controller = "My", action = "DoSomething" });

I expect to get a simple URL: http://example.com/dosomething, but it does not work. I tried two methods:

1) routeLinker.GetUri(c => c.DoSomething(null)) - throws NullReferenceException

2) routeLinker.GetUri(c => c.DoSomething(new SomeDto())) - generates invalid URL: http://example.com/dosomething?someDto=Namespace.SomeDto

Update: Issue opened at github: https://github.com/ploeh/Hyprlinkr/issues/17


Solution

  • I found a workaround, loosely based on Mark's answer. The idea is to go over every route parameter and remove those that have [FromBody] attribute applied to them. This way dispatcher does not need to be modified for every new controller or action.

    public class BodyParametersRemover : IRouteDispatcher {
        private readonly IRouteDispatcher _defaultDispatcher;
    
        public BodyParametersRemover(String routeName) {
            if (routeName == null) {
                throw new ArgumentNullException("routeName");
            }
            _defaultDispatcher = new DefaultRouteDispatcher(routeName);
        }
    
        public Rouple Dispatch(
            MethodCallExpression method,
            IDictionary<string, object> routeValues) {
    
            var routeKeysToRemove = new HashSet<string>();
            foreach (var paramName in routeValues.Keys) {
                var parameter = method
                    .Method
                    .GetParameters()
                    .FirstOrDefault(p => p.Name == paramName);
                if (parameter != null) {
                    if (IsFromBodyParameter(parameter)) {
                        routeKeysToRemove.Add(paramName);
                    }
                }
            }
            foreach (var routeKeyToRemove in routeKeysToRemove) {
                routeValues.Remove(routeKeyToRemove);
            }
            return _defaultDispatcher.Dispatch(method, routeValues);
        }
    
        private Boolean IsFromBodyParameter(ParameterInfo parameter) {
            var attributes = parameter.CustomAttributes;
            return attributes.Any(
                ct => ct.AttributeType == typeof (FromBodyAttribute));
        }
    }