在 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
我试图通过覆盖 ApiController
的 ExecuteAsync
方法来解决这个问题,但到目前为止还没有运气。关于这个问题有什么建议吗?
编辑:我忘了提到现在我正试图在 ASP.NET Web API 上移动这段代码,它有不同的路由方法。问题是,如何使代码在 ASP.NET Web API 上运行?
这是我发现支持额外 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 属性。
从这里开始:
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;
}
我在一个自托管的 WEB API 服务应用程序中尝试了这个,它就像一个魅力:)
[HttpGet]
、[HttpPost]
等属性装饰它们以将动词映射到方法。
我发现属性使用起来比通过代码手动添加更干净。这是一个简单的例子。
[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 }
);
一些好的链接 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
config.MapHttpAttributeRoutes();
添加到我的 WebApiConfig.cs
中,并在我的 WebApiApplication.Application_Start()
方法的末尾添加 GlobalConfiguration.Configuration.EnsureInitialized();
以使路由属性起作用。
config.MapHttpAttributeRoutes();
需要出现在路由映射之前(例如,在 config.Routes.MappHttpRoute(...
.
在 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();
}
您可以像上面提到的那样进行多次获取。
您需要在 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 }
);
使用较新的 Web Api 2,拥有多个 get 方法变得更加容易。
如果传递给 GET
方法的参数足够不同,属性路由系统可以区分它们的类型,就像 int
和 Guid
的情况一样,您可以在 [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/
在 ASP.NET Core 2.0 中,您可以将 Route 属性添加到控制器:
[Route("api/[controller]/[action]")]
public class SomeController : Controller
{
public SomeValue GetItems(CustomParam parameter) { ... }
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;}
打电话给他们:
本地主机:5000/api/controllername/method1-42
“你好42”
本地主机:5000/api/controllername/method2-99
“世界99”
我试图使用 Web Api 2 属性路由来允许多个 Get 方法,并且我已经结合了以前答案中的有用建议,但是在控制器中我只修饰了“特殊”方法(示例):
[Route( "special/{id}" )]
public IHttpActionResult GetSomethingSpecial( string id ) {
...无需在控制器顶部放置 [RoutePrefix] :
[RoutePrefix("api/values")]
public class ValuesController : ApiController
我收到错误消息,指出找不到与提交的 URI 匹配的路由。一旦我将 [Route] 装饰方法以及 [RoutePrefix] 作为一个整体装饰控制器,它就起作用了。
默认情况下 [Route("api/[controller]") 将由 .Net Core/Asp.Net Web API 生成。您需要稍作修改,只需添加 [Action] 像 [Route("api/[controller]/[行动]”)]。我提到了一个虚拟的解决方案:
// 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");
}
}
我不确定你是否找到了答案,但我这样做了,它有效
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 }
);
您是否尝试过切换到 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 应该处理它,但我已经看到它在多个 Get 方面存在一些问题,更不用说具有相同返回类型的多个 Get。
[编辑:这些都不适用于 WCF WebAPI 的日落和迁移到 MVC 堆栈上的 ASP.Net WebAPI]
**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) { ... }
}
在 [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]
,所以我花了一段时间才弄清楚。
以上示例均不适用于我的个人需求。以下是我最终做的。
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 方法。那里的“真”表示我们要检查数组中项目的匹配。如果它是假的,你会说排除那些在 str 你可以使用高于此默认方法的路由,例如:
config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" });
在上面它本质上是在寻找以下 URL => http://www.domain.com/Account/Status/Active
或类似的东西。
除了上述之外,我不确定我会变得太疯狂。归根结底,它应该是每个资源。但我确实看到出于各种原因需要映射友好的 url。我很确定随着 Web Api 的发展,将会有某种规定。如果有时间,我会建立一个更永久的解决方案并发布。
new System.Web.Http.Routing.HttpMethodConstraint(HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete)
。
无法使上述任何路由解决方案工作——一些语法似乎已经改变,我对 MVC 还是新手——尽管我把这个非常糟糕(而且简单)的黑客放在一起,这会让我受益匪浅到现在为止——注意,这替换了“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;
}
如果您在同一个文件中有多个 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 }
);
简单的替代方案
只需使用查询字符串。
路由
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”或配置路由中的任何参数。
单个 asp.net web api 控制器中的多个方法的概念使得在代码中拥有多个方法变得更加容易。
我能够按照上述解决方案中的步骤实施,并提出了这个最终代码
在 WebApiConfig.cs 中,按此顺序设置以下路由配置
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();
}
}
然后在您的控制器中使用 [ActionName] 引用 [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
修改 WebApiConfig 并在末尾添加另一个 Routes.MapHttpRoute,如下所示:
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";
}
}
这就是我解决它的方法。我希望它会帮助某人。
constraints: new{id=@"\d+"}
Put
方法添加一个像routes.MapHttpRoute("DefaultApiPut", "Api/{controller}", new {action = "Put"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Put)});
这样的额外路由,否则它会给我 404。