【问题标题】:Adding multiple custom "get" calls to Restful API向 Restful API 添加多个自定义“get”调用
【发布时间】:2020-05-14 11:15:46
【问题描述】:

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

我创建了一个原型,并且基本的 html 调用工作正常。然后我想转向其他类型的电话。我看过很多文章和 sn-ps,但似乎都没有真正为我回答这个问题。我正在使用 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)
{
}

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

-------------更新------------...已将代码修改如下。

“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

【问题讨论】:

  • 如何称呼这些动作?我的意思是,你想让他们有不同的路线吗?还是您希望两者都可以从完全相同的路由/URL 访问?
  • 我确实希望它们是不同的路线。我已经修改了代码以使用 ActionRoute(如上所示),但它仍然无法正常工作。

标签: c# html rest api asp.net-web-api2


【解决方案1】:

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

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

1。 Attribute Routing

从配置中删除 ApiByName 路由。然后您可以使用完整路径(例如[Route("api/ListItems/GetToken")])装饰每个动作,使用[RoutePrefix("api/ListItems")] 装饰控制器并使用Route("MyAction") 装饰动作。下面的示例在控制器上使用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") 将重定向到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 模式,那么您应该真正构建您的端点以匹配它们所作用/为之的资源。您可能应该有一个Invoice 控制器,您可以在其中为Invoices 执行GetPostPutDelete 等。 GetToken 似乎属于不同的控制器,除非它与发票有某种关系。

【讨论】:

  • 感谢您的反馈。我同意令牌应该在它自己的控制器中并且可以做到这一点,但暂时忽略这一点,只考虑两个方法调用。正如您所指出的,我已经读过,名称不会影响外部 URL,我最初认为它会。就是说,但是我已经重新阅读了上面的 MS 文档并对其进行了相应的修改,如您在上面显示的那样,它仍然不会到达正确的终点。我还尝试添加和删除 name 属性以及添加 [ActionName] ,但最终仍然会使用我拥有的基本 Get ..太令人沮丧了。
  • 感谢 haldo 和其他人的所有工作。在进一步搜索时,我最终还找到了 Darin Dimitrov 的帖子。添加了配置更改,删除了 Route 和 Get 引用并添加了 ActionName,它就成功了。我打算发布更新,但你也打败了我:)再次感谢大家的帮助!
  • 就适当的 Restful 而言,是的,这是一个原型,只是为了消除潜在的陷阱。同意令牌属于它自己的控制器。发票以及其他一些方法本身。
【解决方案2】:

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

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

将所有 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,它们不符合您的要求,因此您应该验证输入

【讨论】:

  • 推测,GetToken 将不同的有效负载返回给GetInvoices。如果它们被合并到一个端点,API 的消费者将如何处理呢?无论如何,这似乎并不遵循 IMO 的 RESTful 模式。
  • 确实不理想,这就是为什么我最初建议它可能是设计问题,也许将令牌和发票分开到不同的端点?这听起来像是一个更合理的流程,例如 Token/Get,然后是 Invoice/Get 与您获得的令牌。当然这只是猜测,因为我对您的应用程序一无所知
【解决方案3】:

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

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

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

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

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

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

【讨论】:

  • 我怀疑如果没有进一步的配置就可以工作,你测试过吗?此外,您似乎从我的答案中复制了 URL,而不是从问题中复制了 :)
  • 我确实搞砸了复制/粘贴,你是对的。但是,我的其余答案是RouteAttribute 的标准用法。我从您的帖子中意外复制的唯一 URL 差异是 YearMonth 的格式,因为 OP 中的 / 在 URL 路径中会出现问题。
猜你喜欢
  • 2014-12-09
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 2012-12-18
  • 2015-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多