向 Restful API 添加多个自定义 "get" 调用

Adding multiple custom "get" calls to Restful API

我只使用过几个 Restful API,并且一直需要创建一个。最终我需要一个 API 并希望 Restful 方面,但那部分不是最终的焦点。

我创建了一个原型并让基本的 html 调用工作正常。然后我想转到其他类型的电话。我看过很多文章和片段,但其中 none 似乎真的能回答我的问题。我正在使用 Postman 进行测试。现在我有两个基本调用 "Get" 调用,它们都带有三个字符串参数,意识到它们具有相同的签名但名称不同。我收到与歧义相关的错误。任何帮助表示赞赏

以下是我的设置:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HttpConfiguration();

        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "ApiByName",
            routeTemplate: "api/{controller}/{name}",
            defaults: null,
            constraints: new { name = @"^[a-z]+$" }
        );

        config.Formatters.Remove(config.Formatters.XmlFormatter);
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;

        app.UseWebApi(config);
    }
}

下面是我的两个方法调用

[Route("{UserId}/{Key}/{Source}", Name = "GetToken")]
[HttpGet]
public HttpResponseMessage GetToken(string UserId, string Key)
{
}

[Route("{Token}/{AcctNo}/{YearMonth}", Name = "GetInvoices")]
[HttpGet]
public HttpResponseMessage GetInvoices(string Token, string AcctNo, string YearMonth)
{
}

如果我注释掉其中一个,另一个将被调用,但我似乎无法让它工作。

------------Update------------...修改了代码如下。

"ApiByName" 已从配置启动中删除。以下是方法。我包含了一个我以前没有展示过的通用 get。

public IEnumerable<CustomListItem> Get()
{
    //            return new string[] { "value1", "value2" };
    return _listItems;
}

[Route("GetToken/{UserId}/{Key}/{Source}")]
[HttpGet]
public HttpResponseMessage GetToken(string UserId, string Key, string Source)
{
   ...
}

[Route("GetInvoices/{Token}/{AcctNo}/{YearMonth}")]
[HttpGet]
public HttpResponseMessage GetInvoices(string Token, string AcctNo, string YearMonth)
{
   ...
}

即使进行了上述更改,调用始终转到通用 Get(显示的第一种方法)以下是来自邮递员的调用示例(以及直接粘贴在浏览器中)??

http://localhost:37788/api/listitems/GetToken?UserId=Dshadle&Key=ABC&Source=Postman

http://localhost:37788/api/listitems/GetInvoices?Token=abc&AcctNo=123&YearMonth=2019/12

如果您多次遇到此问题,那是因为您将太多操作放入单个端点。请记住将每个实体(或聚合)的操作分离到它自己的控制器中。

如果您不是这种情况,那么最简单的解决方案是使用查询参数

将所有 6 个参数放入模型中,将其作为 Get 方法的参数传递,并用 [FromQuery] 注释。

像这样:

[HttpGet] 
public async Task<IActionResult> Get([FromQuery] GetRequestParameters parameters)
{
   ... 
}

这样你就可以这样称呼它:

获取https://your_url/api/your_controller?userId=123&key=secret&source=somesource

或者

获取https://your_url/api/your_controller?token=abcd&accountNumber=secret&yearMonth=202001

请注意,您可能会同时收到无意义的参数,例如 token、key 和 userId,这些参数不符合您的要求,因此您应该验证输入

您似乎想将 api/ListItems/GetToken 映射到 GetToken() 端点,对于 GetInvoices 也是如此。根据您共享的 URL,我假设您的控制器名为 ListItems

实际上有两种方法可以做到这一点。无论您选择使用哪种方法,您都不需要将参数添加到您显示的 URL 的路由中。

1。 Attribute Routing

从配置中删除 ApiByName 路由。然后,您可以用完整路径(例如 [Route("api/ListItems/GetToken")])、 装饰每个动作,用 [RoutePrefix("api/ListItems")] 装饰控制器,并用 [= 装饰动作21=]。下面的示例在控制器上使用 RoutePrefix

[RoutePrefix("api/ListItems")]
public class ListItemsController : ApiController
{     

  [Route("GetToken")]
  [HttpGet]
  public HttpResponseMessage GetToken(string UserId, string Key, string source)

  [Route("GetInvoices", Name = "GetInvoices")]
  [HttpGet]
  public HttpResponseMessage GetInvoices(string Token, string AcctNo, string YearMonth)

如果您想要一个看起来像 ListItems/GetToken/123/ABC/Postman 的 URL,那么只需将参数添加回路由 [Route("GetToken/{UserId}/{Key}/{Source}")],但是,您发布的 URL 不会匹配该格式。

Route 属性的 Name 属性 可能无法像您预期的那样工作。它实际上用于定义 API 内部的端点,而不是外部的。例如,RedirectToRoute("GetInvoices") 将 return 重定向到 GetInvoices 端点。是否需要包含它取决于您。

2。 Conventional Routing

根本不要使用 [Route] 属性。

相反,将以下路由添加到 API 路由配置(感谢 Darin Dimitrov 的 answer):

config.Routes.MapHttpRoute(
     name: "ApiById",
     routeTemplate: "api/{controller}/{id}",
     defaults: new { id = RouteParameter.Optional },
     constraints: new { id = @"^[0-9]+$" }
 );

config.Routes.MapHttpRoute(
    name: "ApiByAction",
    routeTemplate: "api/{controller}/{action}",
    defaults: new { action = "Get" }
);

然后在控制器中使用ActionName("MyAction")属性:

[HttpGet]
[ActionName("GetToken")]
public HttpResponseMessage GetToken(string UserId, string Key, string source)

[HttpGet]
[ActionName("GetInvoices")]
public HttpResponseMessage GetInvoices(string Token, string AcctNo, string YearMonth)

我已经使用以下 URL 测试了这两种方法:

// get list of items
http://localhost:28092/api/ListItems    

// get single item 
http://localhost:28092/api/ListItems/1  

// GetToken 
http://localhost:28092/api/ListItems/GetToken?userid=123&key=ABC&source=Postman

// GetInvoices
http://localhost:28092/api/ListItems/GetInvoices?token=ABC&acctno=1234&yearmonth=2019-12

注意 1:我建议 不要 使用 / 作为URL 中的日期分隔符,因为它可能会干扰路由,请改用 - 或其他分隔符。

注2:参数必须有值,否则框架将无法找到正确的动作。如果(某些)参数应该是可选的,则在方法签名中指定默认值,例如 GetToken(string UserId, string Key, string source = "")source 在这里是可选的)。


但是,如果您想遵循 RESTful 模式,那么您应该真正构建端点以匹配它们所作用的资源 on/for。您可能应该有一个 Invoice 控制器,您可以在其中为 Invoice 执行 GetPostPutDelete 等。 GetToken 似乎它属于不同的控制器,除非它以某种方式与发票相关。

你最后的方法是正确的,但这里的问题是你混合了 2 个方案。

您提供此设置:[Route("GetInvoices/{Token}/{AcctNo}/{YearMonth}")]

然后你转身使用这样的查询参数进行测试

/api/ListItems/GetInvoices?token=ABC&acctno=1234&yearmonth=2019-12

但是如果你看你的Route属性,它表明这个动作的路线应该在路径中完成。相反,更改您正在测试的 URL 以匹配路径,如下所示:

/api/ListItems/GetInvoices/ABC/1234/2019-12