ASP.NET Web API 中具有多个 GET 方法的单个控制器

在 Web API 中,我有一类类似的结构:

public class SomeController : ApiController
{
[WebGet(UriTemplate = "{itemSource}/Items")]
public SomeValue GetItems(CustomParam parameter) { ... }


[WebGet(UriTemplate = "{itemSource}/Items/{parent}")]
public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

因为我们可以映射单个方法,所以在正确的地方获得正确的请求非常简单。对于只有一个 GET方法但也有一个 Object参数的类,我成功地使用了 IActionValueBinder。但是,在上面描述的情况下,我得到了以下错误:

Multiple actions were found that match the request:


SomeValue GetItems(CustomParam parameter) on type SomeType


SomeValue GetChildItems(CustomParam parameter, SomeObject parent) on type SomeType

我试图通过覆盖 ApiControllerExecuteAsync方法来解决这个问题,但是到目前为止还没有找到。在这个问题上有什么建议吗?

编辑: 我忘了提到,现在我正试图在 ASP.NET Web API 上移动这个代码,它有一个不同的路由方法。问题是,如何使代码在 ASP.NET Web API 上工作?

293765 次浏览

您是否尝试过切换到 WebInvokeAttribute 并将方法设置为“ GET”?

我相信我也遇到过类似的问题,所以我转而明确地告诉大多数方法(如果不是全部的话)需要使用哪个方法(GET/PUT/POST/DELETE)。

public class SomeController : ApiController
{
[WebInvoke(UriTemplate = "{itemSource}/Items"), Method="GET"]
public SomeValue GetItems(CustomParam parameter) { ... }


[WebInvoke(UriTemplate = "{itemSource}/Items/{parent}", Method = "GET")]
public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

WebGet应该处理它,但是我看到它有一些问题与多个获取更少的多个获取相同的返回类型。

[编辑: 随着 WCF WebAPI 的日落和 MVC 栈上向 ASP.Net WebAPI 的迁移,这些都无效]

您需要像下面这样在 global.asax.cs 中定义更多的路由:

routes.MapHttpRoute(
name: "Api with action",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);


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

我不知道你是否已经找到了答案,但我这样做了,它的工作

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


// GET /api/values/5
public string Get(int id)
{
return "value";
}


// GET /api/values/5
[HttpGet]
public string GetByFamily()
{
return "Family value";
}

现在是 global.asx

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");


routes.MapHttpRoute(
name: "DefaultApi2",
routeTemplate: "api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);


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


routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

从这里开始:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });

这样说:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });

因此,现在可以指定要将 HTTP 请求发送到哪个操作(方法)。

张贴到 “ http://localhost:8383/api/command/postcreateuser”调用:

public bool PostCreateUser(CreateUserCommand command)
{
//* ... *//
return true;
}

并发送到 “ http://localhost:8383/api/command/postmakebooking”调用:

public bool PostMakeBooking(MakeBookingCommand command)
{
//* ... *//
return true;
}

我在一个自托管的 WEBAPI 服务应用程序中尝试了这种方法,它的效果非常好:)

这是我发现的支持额外 GET 方法和支持普通 REST 方法的最佳方法。将以下路由添加到 WebApiConfig:

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

我用下面的测试类验证了这个解决方案。我能够成功地点击下面我的控制器中的每个方法:

public class TestController : ApiController
{
public string Get()
{
return string.Empty;
}


public string Get(int id)
{
return string.Empty;
}


public string GetAll()
{
return string.Empty;
}


public void Post([FromBody]string value)
{
}


public void Put(int id, [FromBody]string value)
{
}


public void Delete(int id)
{
}
}

我证实它支持以下请求:

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

注意如果你额外的 GET 操作没有以“ GET”开头,你可能需要在方法中添加一个 HttpGet 属性。

以上的例子都不能满足我的个人需求。

 public class ContainsConstraint : IHttpRouteConstraint
{
public string[] array { get; set; }
public bool match { get; set; }


/// <summary>
/// Check if param contains any of values listed in array.
/// </summary>
/// <param name="param">The param to test.</param>
/// <param name="array">The items to compare against.</param>
/// <param name="match">Whether we are matching or NOT matching.</param>
public ContainsConstraint(string[] array, bool match)
{


this.array = array;
this.match = match;
}


public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (values == null) // shouldn't ever hit this.
return true;


if (!values.ContainsKey(parameterName)) // make sure the parameter is there.
return true;


if (string.IsNullOrEmpty(values[parameterName].ToString())) // if the param key is empty in this case "action" add the method so it doesn't hit other methods like "GetStatus"
values[parameterName] = request.Method.ToString();


bool contains = array.Contains(values[parameterName]); // this is an extension but all we are doing here is check if string array contains value you can create exten like this or use LINQ or whatever u like.


if (contains == match) // checking if we want it to match or we don't want it to match
return true;
return false;


}

在你的路线中使用以上方法:

config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { action = RouteParameter.Optional, id = RouteParameter.Optional}, new { action = new ContainsConstraint( new string[] { "GET", "PUT", "DELETE", "POST" }, true) });

所发生的是方法中的约束类型假设,这样这个路由将只匹配默认的 GET、 POST、 PUT 和 DELETE 方法。这里的“ true”表示我们要检查数组中的项是否匹配。如果它是 false,你会说排除 strI 中的那些路由,然后你可以使用这个默认方法上面的路由,比如:

config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" });

在上面的代码中,它实际上是在寻找下面的 URL = > http://www.domain.com/Account/Status/Active或类似的东西。

除此之外,我不确定我是否会变得太疯狂。归根结底,它应该是每个资源。但出于各种原因,我确实看到了对友好 url 进行映射的必要性。我非常确定随着 Web Api 的发展,将会有某种形式的规定。如果时间,我会建立一个更永久的解决方案和职位。

上面的路由解决方案都不能正常工作——一些语法似乎已经改变了,而我对 MVC 还是个新手——在紧要关头,尽管我把这个非常糟糕(和简单)的黑客技巧放在一起,这个技巧暂时能让我过关——注意,这个技巧取代了“ public MyObject GetMyObjects (long id)”方法——我们把“ id”的类型改为字符串,把返回类型改为 object。

// GET api/MyObjects/5
// GET api/MyObjects/function
public object GetMyObjects(string id)
{
id = (id ?? "").Trim();


// Check to see if "id" is equal to a "command" we support
// and return alternate data.


if (string.Equals(id, "count", StringComparison.OrdinalIgnoreCase))
{
return db.MyObjects.LongCount();
}


// We now return you back to your regularly scheduled
// web service handler (more or less)


var myObject = db.MyObjects.Find(long.Parse(id));
if (myObject == null)
{
throw new HttpResponseException
(
Request.CreateResponse(HttpStatusCode.NotFound)
);
}


return myObject;
}

我发现使用属性比通过代码手动添加属性更简单。

[RoutePrefix("api/example")]
public class ExampleController : ApiController
{
[HttpGet]
[Route("get1/{param1}")] //   /api/example/get1/1?param2=4
public IHttpActionResult Get(int param1, int param2)
{
Object example = null;
return Ok(example);
}


}

您还需要在 webapiconfig 中使用它

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


config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
< p > 一些好的连结 http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api 这个更能解释路由。 http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

如果在同一个文件中有多个 Action,那么将相同的参数传递给所有 Action,例如 Id。这是因为 action 只能标识 Id,所以不需要给参数赋任何名称,只需像下面这样声明 Id。


[httpget]
[ActionName("firstAction")] firstAction(string Id)
{.....
.....
}
[httpget]
[ActionName("secondAction")] secondAction(Int Id)
{.....
.....
}
//Now go to webroute.config file under App-start folder and add following
routes.MapHttpRoute(
name: "firstAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);


routes.MapHttpRoute(
name: "secondAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

修改 WebApiConfig并在末尾添加另一条路由:

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

然后像这样创建一个控制器:

public class ServiceController : ApiController
{
[HttpGet]
public string Get(int id)
{
return "object of id id";
}
[HttpGet]
public IQueryable<DropDownModel> DropDowEmpresa()
{
return db.Empresa.Where(x => x.Activo == true).Select(y =>
new DropDownModel
{
Id = y.Id,
Value = y.Nombre,
});
}


[HttpGet]
public IQueryable<DropDownModel> DropDowTipoContacto()
{
return db.TipoContacto.Select(y =>
new DropDownModel
{
Id = y.Id,
Value = y.Nombre,
});
}


[HttpGet]
public string FindProductsByName()
{
return "FindProductsByName";
}
}

我就是这么解决的,希望能帮到别人。

随着更新的 WebApi2的出现,拥有多个 get 方法变得更加容易。

如果传递给 GET方法的参数足够不同,使得属性路由系统能够像 intGuid那样区分它们的类型,那么您可以在 [Route...]属性中指定预期的类型

例如-

[RoutePrefix("api/values")]
public class ValuesController : ApiController
{


// GET api/values/7
[Route("{id:int}")]
public string Get(int id)
{
return $"You entered an int - {id}";
}


// GET api/values/AAC1FB7B-978B-4C39-A90D-271A031BFE5D
[Route("{id:Guid}")]
public string Get(Guid id)
{
return $"You entered a GUID - {id}";
}
}

有关此方法的详细信息,请参阅此处的 http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

另一个选项是为 GET方法提供不同的路由。

    [RoutePrefix("api/values")]
public class ValuesController : ApiController
{
public string Get()
{
return "simple get";
}


[Route("geta")]
public string GetA()
{
return "A";
}


[Route("getb")]
public string GetB()
{
return "B";
}
}

详情请参阅此处 -http://nodogmablog.bryanhogan.net/2016/10/web-api-2-controller-with-multiple-get-methods/

我试图使用 Web Api 2属性路由来支持多个 Get 方法,并且我已经整合了前面答案中的有用建议,但是在 Controller 中我只修饰了“特殊”方法(示例) :

[Route( "special/{id}" )]
public IHttpActionResult GetSomethingSpecial( string id ) {

... 同时在控制器的顶部放置一个[ RoutePrefix ] :

[RoutePrefix("api/values")]
public class ValuesController : ApiController

我收到错误信息,说没有找到与提交的 URI 匹配的路由。一旦我有了[ Route ]装饰方法和[ RoutePrefix ]装饰 Controller 作为一个整体,它就工作了。

在 ASP.NET Core 2.0中,您可以向控制器添加 路线属性:

[Route("api/[controller]/[action]")]
public class SomeController : Controller
{
public SomeValue GetItems(CustomParam parameter) { ... }


public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

简单的选择

只需使用查询字符串。

路由

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

控制员

public class TestController : ApiController
{
public IEnumerable<SomeViewModel> Get()
{
}


public SomeViewModel GetById(int objectId)
{
}
}

请求

GET /Test
GET /Test?objectId=1

注意

请记住,查询字符串参数不应该是“ id”或配置路由中的任何参数。

**Add Route function to direct the routine what you want**
public class SomeController : ApiController
{
[HttpGet()]
[Route("GetItems")]
public SomeValue GetItems(CustomParam parameter) { ... }


[HttpGet()]
[Route("GetChildItems")]
public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

懒惰/匆忙的替代方案(Dotnet Core 2.2) :

[HttpGet("method1-{item}")]
public string Method1(var item) {
return "hello" + item;}


[HttpGet("method2-{item}")]
public string Method2(var item) {
return "world" + item;}

呼唤他们:

Localhost: 5000/api/controller/method1-42

“你好42”

Localhost: 5000/api/controller/method2-99

“ World 99”

[Route]属性中指定基本路径,然后在 [HttpGet]中添加到基本路径,这对我来说很有用。你可以试试:

    [Route("api/TestApi")]      //this will be the base path
public class TestController : ApiController
{
[HttpGet]  //example call: 'api/TestApi'
public string Get()
{
return string.Empty;
}
    

[HttpGet("{id}")]  //example call: 'api/TestApi/4'
public string GetById(int id) //method name won't matter
{
return string.Empty;
}
    

//....

我花了一段时间才弄明白,因为我不想多次使用 [Route]

在 VS 2019中,这种方法很容易实现:

[Route("api/[controller]/[action]")] //above the controller class

密码是:

[HttpGet]
[ActionName("GetSample1")]
public Ilist<Sample1> GetSample1()
{
return getSample1();
}
[HttpGet]
[ActionName("GetSample2")]
public Ilist<Sample2> GetSample2()
{
return getSample2();
}
[HttpGet]
[ActionName("GetSample3")]
public Ilist<Sample3> GetSample3()
{
return getSample3();
}
[HttpGet]
[ActionName("GetSample4")]
public Ilist<Sample4> GetSample4()
{
return getSample4();
}

您可以像上面提到的那样有多个 get。

默认情况下[ Route (“ api/[ controller ]”)将由.Net Core/Asp. Net Web API 生成。您需要稍微修改一下,只需添加[ Action ] ,如[ Route (“ api/[ controller ]/[ Action ]”)]。 我提到了一个虚拟的解决方案:
// Default generated controller
//
[Route("api/[controller]")
public class myApiController : Controller
{
[HttpGet]
public string GetInfo()
{
return "Information";
}
}


//
//A little change would do the magic
//


[Route("api/[controller]/[action]")]
public class ServicesController : Controller
{
[HttpGet]
[ActionName("Get01")]
public string Get01()
{
return "GET 1";
}


[HttpGet]
[ActionName("Get02")]
public string Get02()
{
return "Get 2";
}
    

[HttpPost]
[ActionName("Post01")]
public HttpResponseMessage Post01(MyCustomModel01 model)
{
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        

//.. DO Something ..
return Request.CreateResonse(HttpStatusCode.OK, "Optional Message");
}
    

[HttpPost]
[ActionName("Post02")]
public HttpResponseMessage Post02(MyCustomModel02 model)
{
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        

//.. DO Something ..
return Request.CreateResonse(HttpStatusCode.OK, "Optional Message");
}




}

在一个 asp.net web API 控制器中使用多个方法的概念使得在代码中使用多个方法变得更加容易。

我能够实现以上解决方案中的步骤,并得出了这个最终代码

在 WebApiConfig.cs 中,按照以下顺序设置下面的 Route 配置

 public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services


// Web API routes
config.Routes.MapHttpRoute(
name: "DefaultApiAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);


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


config.MapHttpAttributeRoutes();




}
}

然后在控制器中引用[ HttpGet ] for GET 或[ HttpPost ] for POST with [ ActionName ]查看下面的示例代码

namespace WebRESTApi.Controllers
{
//[RoutePrefix("api/Test")]
public class TestController : ApiController
{


 



[HttpGet]
[ActionName("AllEmailWithDisplayname")]
public string AllEmailWithDisplayname()
{
          

return "values";
}




 



[HttpPost]
[ActionName("Authenticate")]
// POST: api/Authenticate
public object Authenticate([FromBody()] object Loginvalues)
{
 

return true;
        

}




[HttpPost]
[ActionName("ShowCredential")]
// POST:  api/Showcredential
public object Showcredential([FromBody()] object Loginvalues)
{


            

return "Username: "




}






}
}

然后,您可以使用以下格式通过客户端或邮递员使用不同的方法

Http://url/api/controller/actionname