似乎有一千人在堆栈溢出时问了同样的问题,但似乎没有一个解决这个问题的方法。我要再问一次......
我有一个API控制器,它具有以下操作:
// GET api/Exploitation
public HttpResponseMessage Get() {
var items = _exploitationRepository.FindAll();
var mappedItems = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(items);
var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItems);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { }));
return response;
}
// GET api/Exploitation/5
[HttpGet, ActionName("Get")]
public HttpResponseMessage Get(int id) {
var item = _exploitationRepository.FindById(id);
var mappedItem = Mapper.Map<Exploitation, ExploitationView>(item);
var response = Request.CreateResponse<ExploitationView>(HttpStatusCode.OK, mappedItem);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
return response;
}
// GET api/Exploitation/GetBySongwriterId/5
[HttpGet, ActionName("GetBySongwriterId")]
public HttpResponseMessage GetBySongwriterId(int id) {
var item = _exploitationRepository.Find(e => e.Song.SongWriterSongs.Any(s => s.SongWriterId == id))
.OrderByDescending(e => e.ReleaseDate);
var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);
var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
return response;
}
// GET api/Exploitation/GetBySongwriterId/5
[HttpGet, ActionName("GetBySongId")]
public HttpResponseMessage GetBySongId(int id) {
var item = _exploitationRepository.Find(e => e.SongId == id)
.OrderByDescending(e => e.ReleaseDate);
var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);
var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
return response;
}
在我的APIConfig中,我定义了以下路线:
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, action = RouteParameter.Optional },
constraints: new { id = @"\d+" }
);
我发现我可以访问以下操作没问题: / API /开发 / API /开发/ getbysongwriterid / 1 / API /开发/ getbysongid / 1
当我尝试访问/ api / exploit / 1时,我得到了这个异常
"Multiple actions were found that match the request: System.Net.Http.HttpResponseMessage Get(Int32) on type Songistry.API.ExploitationController System.Net.Http.HttpResponseMessage GetBySongwriterId(Int32)" exception.
有人能看出我的路线有什么问题吗?或其他别的错误?
答案 0 :(得分:6)
我找到了一个优雅的解决方案。
我将ApiRouteConfig修改为具有以下路线:
config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = @"\d+" }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
现在我可以访问:
/api/exploitation
/api/exploitation/1
/api/exploitation/getbysongid/1
/api/exploitation/getbysongwriterid/1
我根本不需要修改我的控制器操作来使用这个新的路由配置。
如果你有多个PUT或POST动作,你可以创建如下所示的新路线:
config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Put" },
constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Put) }
);
config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Delete" },
constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Delete) }
);
我希望这个答案可以帮助每个人,因为这似乎是人们常见的问题。
答案 1 :(得分:0)
您遇到的问题是 / api / exploit / 1 属于:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
所有GET方法都满足该路由,特别是因为 {id} 是可选的,并且它们的控制器是相同的。
所以你有一个来自客户端的HTTP GET请求和多个接受GET请求的方法。它不知道要去哪一个。
api/{controller}/{action}/{id}
//This works fine because you specified which action explicitly
我希望能回答你的问题。
答案 2 :(得分:0)
在路线定义中尝试以下操作。只保留以下路线:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { id = @"\d+" }
);
将第一个Get方法设为私有,修改第二个方法,使id具有默认值:
// GET api/Exploitation
private HttpResponseMessage Get() {
// implementation stays the same but now it's private
}
// GET api/Exploitation/5
[HttpGet, ActionName("Get")]
public HttpResponseMessage Get(int id = 0) {
if (id == 0) {
return Get();
}
// continue standard implementation
}
这种方式(我自己没有测试过)我希望:
这可能有用。更严格的路线定义实际上可能是这样的:
config.Routes.MapHttpRoute(
name: "ApiWithRequiredId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: null /* make sure we have explicit action and id */,
constraints: new { id = @"\d+" }
);
config.Routes.MapHttpRoute(
name: "ApiWithOptionalId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { action = "Get" /* only allow Get method to work with an optional id */, id = @"\d+" }
);
但是这些方面的东西......尝试一下我希望它能解决你的问题。