目前,我尝试实现.NET Core 2.0 Web API Web挂钩。
发送服务器上的webhook可以简单地配置为:
"endpoint": "http://localhost:50100/api/Hook"
发送服务器做两件事:
我实现了一个HookController并且知道我面临一个(对我而言)奇怪的路由问题。
控制器实现如下:
[Route("api/Hook")]
public class HookController : Controller
{
protected ILogger<HookController> Logger { get; set; }
public HookController(ILogger<HookController> 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();
}
}
问题很简单:
AmbiguousActionException:匹配多个动作。以下操作匹配路由数据并满足所有约束:
那么这两种方法之间有什么区别(web api说没有),我如何将它们组合成一种方法(因为服务器配置让我只定义一种方法)?
基于Lennarts的输入,我得到了一份有效的实施草案。
public class HookController : Controller
{
protected ILogger<HookController> Logger { get; set; }
public HookController(ILogger<HookController> 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<JObject>(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();
}
}
非常感谢Lennart给予您极大的帮助和耐心。
好吧你可以定义为一个不同的类型,使用[HttpGet]
为HookReceiverLookUp()
和[HttpPost]
为HookReceiverEvent([FromBody] JObject res)
。
编辑
尝试这样的事情:
[Route("api/Hook")]
[Produces("application/json")]
[Consumes("application/json")]
public class HookController : Controller
{
private ILogger<HookController> Logger { get; set; }
public HookController(ILogger<HookController> logger)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
return Request.Headers.Any(
header => header.Key == "HeaderContentType" && header.Value == "application/json")
? HookReceiverEvent(Request.GetBody())
: HookReceiverLookUp();
private IActionResult HookReceiverLookUp()
{
Logger.LogInformation("Hook Receiver POST lookup called");
return Ok();
}
private IActionResult HookReceiverEvent(JObject res)
{
Logger.LogInformation("Hook Receiver event");
return Ok();
}
}
然后你还需要一个带扩展方法的类。我看起来像这样:
public static class Extensions
{
public static JObject GetBody(this HttpRequest request)
{
string body;
request.EnableRewind();
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
body = reader.ReadToEnd();
}
request.Body.Position = 0;
return JsonConvert.DeserializeObject<JObject>(body);
}
}