title: .NET Core 2.0 Web API Controller routing (POST with optional [FromBody] Parameter) for receiving web hooks
body: <p>Currently I try to implement a .NET Core 2.0 Web API web hook.</p>
  
  <p>The webhook on the sending server can simply be configured as:</p>
  
  <pre><code>"endpoint": "http://localhost:50100/api/Hook"
  </code></pre>
  
  <p>The sending server does two things:</p>
  
  <ul>
  <li>on startup it sends an empty post to the defined endpoint to check if it is there and which media types are supported</li>
  <li>later on the events are sent as plain json in the post body to the defined endpoint</li>
  </ul>
  
  <p>I implemented a HookController and know I am facing a (for me) strange routing problem.</p>
  
  <p>The controller implementation is as follows:</p>
  
  <pre><code>    [Route("api/Hook")]
  public class HookController : Controller
  {
    protected ILogger&lt;HookController&gt; Logger { get; set; }
  
    public HookController(ILogger&lt;HookController&gt; logger)
    {
      Logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
  
    [HttpPost]
    [Produces("application/json")]
    [Consumes("application/json")]
    public IActionResult HookReceiverLookUp()
    {
      Logger.LogInformation("Hook Receiver POST lookup called");
      return Ok();
    }
  
    [HttpPost]
    [Produces("application/json")]
    [Consumes("application/json")]
    public IActionResult HookReceiverEvent([FromBody] JObject res)
    {
      if (res == null)
      {
        Logger.LogInformation("Lookup called (most likely because post body was null)");
        return Ok();
      }
      Logger.LogInformation("Hook Receiver event");
      return Ok();
    }
  
  }
  </code></pre>
  
  <p>The problem is simple:</p>
  
  <ul>
  <li>If I have only the HookReceiverLookUp method the server recognizes a valid endpoint at startup but obviously this method can't receive data</li>
  <li>If I only have the HookReceiverEvent method it could receive json from POST body but it does not get recognized as a valid endpoint (even with [FromBody] JObject res = null)</li>
  <li>If I have both methods the receiving web api won't start with an exception</li>
  </ul>
  
  <blockquote>
    <p>AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:</p>
  </blockquote>
  
  <hr>
  
  <p>So what are the differences between those two methods (web api says none) and how do I combine them into one method (since the server config let's me only define one method)?</p>
  
  <hr>
  
  <h2>My final solution</h2>
  
  <p>Based on Lennarts inputs I got a working implementation draft up and running.</p>
  
  <pre><code>public class HookController : Controller
  {
    protected ILogger&lt;HookController&gt; Logger { get; set; }
  
    public HookController(ILogger&lt;HookController&gt; logger)
    {
      Logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
  
    //configuration url: http://localhost:[port]/api/Hook/endpoint
    [HttpPost("Endpoint")]
    public IActionResult HookReceiver([FromHeader(Name = "Content-Type")] string type)  //do not bind JObject here
    {
  
      if (type == null) return HookReceiverLookUp();   //check if it is the initial look up call / ignore body
  
      //avoiding built in / default model binder and read the body yourself if necessary
      string body;
  
      using (var reader = new System.IO.StreamReader(Request.Body, System.Text.Encoding.UTF8, true, 1024, true))
      {
        body = reader.ReadToEnd();
      }
  
      //optionally convert it to Json if you want to work with json
      JObject obj = Newtonsoft.Json.JsonConvert.DeserializeObject&lt;JObject&gt;(body);
  
      if (obj == null) return Ok(); //what else??
  
      return HookReceiverEvent(obj);
    }
  
    private IActionResult HookReceiverLookUp()
    {
      Logger.LogInformation("Hook Receiver POST lookup called");
      return Ok();
    }
  
    private IActionResult HookReceiverEvent(JObject res)
    {
      Logger.LogInformation("Hook Receiver POST lookup called\r\n" + res.ToString());
      return Ok();
    }
  
  }
  </code></pre>
  
  <p>A big thank you Lennart for your great help and patience.</p>