C# ASP.NET Web API 中具有多个 GET 方法的单个控制器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9499794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Single controller with multiple GET methods in ASP.NET Web API
提问by paulius_l
In Web API I had a class of similar structure:
在 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) { ... }
}
Since we could map individual methods, it was very simple to get the right request at the right place. For similar class which had only a single GETmethod but also had an Objectparameter, I successfully used IActionValueBinder. However, in the case described above I get the following error:
由于我们可以映射单个方法,因此在正确的位置获取正确的请求非常简单。对于只有一个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
I am trying to approach this problem by overriding the ExecuteAsyncmethod of ApiControllerbut with no luck so far. Any advice on this issue?
我试图通过覆盖ExecuteAsync方法来解决这个问题,ApiController但到目前为止还没有运气。关于这个问题有什么建议吗?
Edit: I forgot to mention that now I am trying to move this code on ASP.NET Web API which has a different approach to routing. The question is, how do I make the code work on ASP.NET Web API?
编辑:我忘了提到现在我试图在 ASP.NET Web API 上移动此代码,它具有不同的路由方法。问题是,如何使代码在 ASP.NET Web API 上工作?
采纳答案by sky-dev
This is the best way I have found to support extra GET methods and support the normal REST methods as well. Add the following routes to your WebApiConfig:
这是我发现支持额外 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)});
I verified this solution with the test class below. I was able to successfully hit each method in my controller below:
我用下面的测试类验证了这个解决方案。我能够成功地在我的控制器中点击下面的每个方法:
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)
{
}
}
I verified that it supports the following requests:
我确认它支持以下请求:
GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1
NoteThat if your extra GET actions do not begin with 'Get' you may want to add an HttpGet attribute to the method.
请注意,如果您的额外 GET 操作不以“Get”开头,您可能需要向该方法添加 HttpGet 属性。
回答by PMontgomery
Have you tried switching over to WebInvokeAttribute and setting the Method to "GET"?
您是否尝试过切换到 WebInvokeAttribute 并将方法设置为“GET”?
I believe I had a similar problem and switched to explicitly telling which Method (GET/PUT/POST/DELETE) is expected on most, if not all, my methods.
我相信我有一个类似的问题,并切换到明确告诉我的大多数方法(如果不是全部)预期使用哪种方法(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) { ... }
}
The WebGet shouldhandle it but I've seen it have some issues with multiple Get much less multiple Get of the same return type.
WebGet应该处理它,但我已经看到它有一些问题,多个 Get 少了多个相同返回类型的多个 Get。
[Edit: none of this is valid with the sunset of WCF WebAPI and the migration to ASP.Net WebAPI on the MVC stack]
[编辑:随着 WCF WebAPI 的日落和在 MVC 堆栈上迁移到 ASP.Net WebAPI,这些都无效]
回答by Alexander Zeitler
You need to define further routes in global.asax.cs like this:
您需要在 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 }
);
回答by Pavan Josyula
I am not sure if u have found the answer, but I did this and it works
我不确定你是否找到了答案,但我做到了,并且有效
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";
}
Now in global.asx
现在在 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 }
);
回答by uggeh
Go from this:
从这里开始:
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
To this:
对此:
config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
Hence, you can now specify which action (method) you want to send your HTTP request to.
因此,您现在可以指定要将 HTTP 请求发送到的操作(方法)。
posting to "http://localhost:8383/api/Command/PostCreateUser"invokes:
发布到“http://localhost:8383/api/Command/PostCreateUser”调用:
public bool PostCreateUser(CreateUserCommand command)
{
//* ... *//
return true;
}
and posting to "http://localhost:8383/api/Command/PostMakeBooking"invokes:
并发布到“http://localhost:8383/api/Command/PostMakeBooking”调用:
public bool PostMakeBooking(MakeBookingCommand command)
{
//* ... *//
return true;
}
I tried this in a self hosted WEB API service application and it works like a charm :)
我在一个自托管的 WEB API 服务应用程序中尝试了这个,它的工作原理非常棒:)
回答by origin1tech
None of the above examples worked for my personal needs. The below is what I ended up doing.
以上示例均不适合我的个人需求。下面是我最终做的。
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;
}
To use the above in your route use:
要在您的路线中使用上述内容,请使用:
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) });
What happens is the constraint kind of fakes in the method so that this route will only match the default GET, POST, PUT and DELETE methods. The "true" there says we want to check for a match of the items in array. If it were false you'd be saying exclude those in the strYou can then use routes above this default method like:
发生的是方法中的约束类型的伪造,因此该路由将仅匹配默认的 GET、POST、PUT 和 DELETE 方法。那里的“true”表示我们要检查数组中的项目是否匹配。如果它是假的,您会说排除 strYou 中的那些然后可以使用此默认方法之上的路由,例如:
config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" });
In the above it is essentially looking for the following URL => http://www.domain.com/Account/Status/Activeor something like that.
在上面它本质上是在寻找以下 URL =>http://www.domain.com/Account/Status/Active或类似的东西。
Beyond the above I'm not sure I'd get too crazy. At the end of the day it should be per resource. But I do see a need to map friendly urls for various reasons. I'm feeling pretty certain as Web Api evolves there will be some sort of provision. If time I'll build a more permanent solution and post.
除了上述之外,我不确定我是否会变得太疯狂。归根结底,它应该是每个资源。但我确实认为出于各种原因需要映射友好的 url。我很确定随着 Web Api 的发展,将会有某种规定。如果有时间我会建立一个更永久的解决方案并发布。
回答by BrainSlugs83
Couldn't make any of the above routing solutions work -- some of the syntax seems to have changed and I'm still new to MVC -- in a pinch though I put together this really awful (and simple) hack which will get me by for now -- note, this replaces the "public MyObject GetMyObjects(long id)" method -- we change "id"'s type to a string, and change the return type to object.
无法使上述任何路由解决方案起作用——有些语法似乎已经改变,我还是 MVC 的新手——虽然我把这个非常糟糕(而且简单)的 hack 放在一起,但它会让我受益现在——注意,这取代了“public MyObject GetMyObjects(long id)”方法——我们将“id”的类型更改为字符串,并将返回类型更改为对象。
// 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;
}
回答by Kalel Wade
I find attributes to be cleaner to use than manually adding them via code. Here is a simple example.
我发现属性比通过代码手动添加它们更易于使用。这是一个简单的例子。
[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);
}
}
You also need this in your webapiconfig
你的 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 }
);
Some Good Links http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-apiThis one explains routing better. http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
一些不错的链接 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
回答by Uttam Kumar
If you have multiple Action within same file then pass the same argument e.g. Id to all Action. This is because action only can identify Id, So instead of giving any name to argument only declare Id like this.
如果您在同一个文件中有多个操作,则将相同的参数(例如 Id)传递给所有操作。这是因为动作只能识别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 }
);
回答by Eduardo Mercado
Modify the WebApiConfigand add at the end another Routes.MapHttpRoute like this:
修改WebApiConfig并在最后添加另一个 Routes.MapHttpRoute,如下所示:
config.Routes.MapHttpRoute(
name: "ServiceApi",
routeTemplate: "api/Service/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Then create a controller like this:
然后创建一个这样的控制器:
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";
}
}
This is how I solved it. I hope it will help someone.
我就是这样解决的。我希望它会帮助某人。

