Один controller с несколькими методами GET в ASP.NET Web API
В Web API у меня был class аналогичной структуры:
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) { ... } }
Поскольку мы могли отображать отдельные методы, было очень просто получить правильный запрос в нужном месте. Для аналогичного classа, который имел только один метод 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
но пока не повезло. Любые советы по этому вопросу?
- Как безопасно вызывать метод async на C # без ожидания
- Как я могу выполнить умножение без оператора '*'?
- objective союзов в C и C ++
- Создайте функцию обертки для malloc и освободите ее на C
- Загрузите и загрузите двоичный файл на / из FTP-сервера в C # /. NET
Edit: Я забыл упомянуть, что теперь я пытаюсь переместить этот код в ASP.NET Web API, который имеет другой подход к маршрутизации. Вопрос в том, как заставить код работать в ASP.NET Web API?
- Неинициализированная локальная переменная - самый быстрый генератор случайных чисел?
- Как проверить, является ли число мощностью 2
- Фильтр / поиск с использованием нескольких полей - ASP.NET MVC
- Как получить использование процессора в streamе в windows (win32)
- c # Использование Parameters.AddWithValue в SqlDataAdapter
- Является ли NULL всегда равным нулю в C?
- Проверка подлинности WinForm UI
- Можно ли выделить функцию внутри массива и вернуть ее с помощью ссылки?
Это лучший способ найти дополнительные методы 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)});
Я проверил это решение с помощью тестового classа ниже. Я смог успешно использовать каждый метод в моем controllerе ниже:
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, и он работает как шарм 🙂
Я считаю, что атрибуты чище использовать, чем вручную добавлять их через код. Вот простой пример.
[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
Вам необходимо определить дальнейшие маршруты в 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 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 и переходе на ASP.Net WebAPI в стек MVC]
С новым Web Api 2 стало проще иметь несколько методов get.
Если параметр, переданный методам GET
, достаточно различен, чтобы система маршрутизации атрибутов различала их типы, как в случае с int
s и 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/
Я пытался использовать маршрутизацию атрибутов Web Api 2, чтобы разрешить несколько методов Get, и я включил полезные предложения из предыдущих ответов, но в controllerе я только украсил «специальный» метод (пример):
[Route( "special/{id}" )] public IHttpActionResult GetSomethingSpecial( string id ) {
… также не помещая [RoutePrefix] в верхней части controllerа:
[RoutePrefix("api/values")] public class ValuesController : ApiController
Я получал ошибки, заявляя, что никакой маршрут не найден в соответствии с представленным URI. Как только у меня был как [Маршрут], украшающий метод, так и [RoutePrefix], украшавший controller в целом, он работал.
В ASP.NET Core 2.0 вы можете добавить атрибут Route к controllerу:
[Route("api/[controller]/[action]")] public class SomeController : Controller { public SomeValue GetItems(CustomParam parameter) { ... } public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... } }
Ни один из приведенных выше примеров не работал для моих личных нужд. Ниже я так и сделал.
public class ContainsConstraint : IHttpRouteConstraint { public string[] array { get; set; } public bool match { get; set; } /// /// Check if param contains any of values listed in array. /// /// The param to test. /// The items to compare against. /// Whether we are matching or NOT matching. 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 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 по умолчанию. «Истина» там говорит, что мы хотим проверить соответствие элементов в массиве. Если бы это было ложно, вы бы сказали, что исключить из них в strYou можно использовать маршруты выше этого метода по умолчанию, например:
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» на строку и меняем тип возвращаемого объекта на объект.
// 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; }
Если у вас несколько действий в одном файле, то передайте тот же аргумент, например 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 } );
Простая альтернатива
Просто используйте строку запроса.
маршрутизация
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
controller
public class TestController : ApiController { public IEnumerable Get() { } public SomeViewModel GetById(int objectId) { } }
Запросы
GET /Test GET /Test?objectId=1
Заметка
Имейте в виду, что параметр строки запроса не должен быть «id» или каким-либо параметром в настроенном маршруте.
Измените WebApiConfig и добавьте в конец еще один Routes.MapHttpRoute, как это:
config.Routes.MapHttpRoute( name: "ServiceApi", routeTemplate: "api/Service/{action}/{id}", defaults: new { id = RouteParameter.Optional } );
Затем создайте controller следующим образом:
public class ServiceController : ApiController { [HttpGet] public string Get(int id) { return "object of id id"; } [HttpGet] public IQueryable DropDowEmpresa() { return db.Empresa.Where(x => x.Activo == true).Select(y => new DropDownModel { Id = y.Id, Value = y.Nombre, }); } [HttpGet] public IQueryable DropDowTipoContacto() { return db.TipoContacto.Select(y => new DropDownModel { Id = y.Id, Value = y.Nombre, }); } [HttpGet] public string FindProductsByName() { return "FindProductsByName"; } }
Вот как я это решил. Надеюсь, это поможет кому-то.