64

I am using Web Api with ASP.NET MVC, and I am very new to it. I have gone through some demo on asp.net website and I am trying to do the following.

I have 4 get methods, with the following signatures

public List<Customer> Get()
{
    // gets all customer
}

public List<Customer> GetCustomerByCurrentMonth()
{
    // gets some customer on some logic
}

public Customer GetCustomerById(string id)
{
    // gets a single customer using id
}

public Customer GetCustomerByUsername(string username)
{
    // gets a single customer using username
}

For all the methods above I would like to have my web api somewhat like as shown below

  • List Get() = api/customers/
  • Customer GetCustomerById(string Id) = api/customers/13
  • List GetCustomerByCurrentMonth() = /customers/currentMonth
  • Customer GetCustomerByUsername(string username) = /customers/customerByUsername/yasser

I tried making changes to routing, but as I am new to it, could'nt understand much.

So, please can some one help me understand and guide me on how this should be done. Thanks

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281

10 Answers10

75

From here Routing in Asp.net Mvc 4 and Web Api

Darin Dimitrov has posted a very good answer which is working for me.

It says...

You could have a couple of routes:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "ApiById",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { id = @"^[0-9]+$" }
        );

        config.Routes.MapHttpRoute(
            name: "ApiByName",
            routeTemplate: "api/{controller}/{action}/{name}",
            defaults: null,
            constraints: new { name = @"^[a-z]+$" }
        );

        config.Routes.MapHttpRoute(
            name: "ApiByAction",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { action = "Get" }
        );
    }
}
Community
  • 1
  • 1
Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281
  • kindly see my [question](https://stackoverflow.com/questions/46680893/passing-multiple-parameters-to-web-api-get-method) – Moeez Oct 11 '17 at 06:23
  • 1
    Very helpful. I created multiple Get methods and did not register them to the WebApiConfig file. Was able to access after registering. – Adi Oct 30 '17 at 10:40
49

First, add new route with action on top:

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

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

Then use ActionName attribute to map:

[HttpGet]
public List<Customer> Get()
{
    //gets all customer
}

[ActionName("CurrentMonth")]
public List<Customer> GetCustomerByCurrentMonth()
{
    //gets some customer on some logic
}

[ActionName("customerById")]
public Customer GetCustomerById(string id)
{
    //gets a single customer using id
}

[ActionName("customerByUsername")]
public Customer GetCustomerByUsername(string username)
{
    //gets a single customer using username
}
cuongle
  • 74,024
  • 28
  • 151
  • 206
  • 1
    aah! this looks promising, will try this out now. – Yasser Shaikh Oct 08 '12 at 05:52
  • I tried this, All works except for `api/customers/` and `/customers/customerByUsername/yasser` what changes should I do – Yasser Shaikh Oct 08 '12 at 06:07
  • i had made a mistake in question, which I have corrected. pls help – Yasser Shaikh Oct 08 '12 at 06:10
  • also now I have two route rules in my WebApiConfig's Register method, first one is the one you have give me and the second one is the one that comes built in. Do I need to remove the second one ? – Yasser Shaikh Oct 08 '12 at 06:30
  • 1
    I am still getting 500 Internal Server Error for `http://localhost:61821/api/customers/`, I really appreciate u helping me out on this one, pls help me again :) – Yasser Shaikh Oct 08 '12 at 06:35
  • @Yasser: this code I have tested on VS, which exception you get? – cuongle Oct 08 '12 at 06:37
  • is it so, let me create a brand new project and try this, may be some setting here and there might be affecting this. Thanks – Yasser Shaikh Oct 08 '12 at 06:41
  • these routes I have to add to `WebApiConfig's Register method` right ? – Yasser Shaikh Oct 08 '12 at 06:59
  • 7
    This looks good on paper, but doesn't actually work. You have to do something like the 2nd answer on this post: http://stackoverflow.com/questions/9569270/custom-method-names-in-asp-net-web-api The trick is adding an int constraint on the id parameter in the first route, and setting the default action to "Get" along with a HttpGet constraint on the second route – Levitikon Apr 01 '13 at 13:18
  • kindly see my [question](https://stackoverflow.com/questions/46680893/passing-multiple-parameters-to-web-api-get-method) – Moeez Oct 11 '17 at 06:23
22

Also you will specify route on action for set route

[HttpGet]
[Route("api/customers/")]
public List<Customer> Get()
{
   //gets all customer logic
}

[HttpGet]
[Route("api/customers/currentMonth")]
public List<Customer> GetCustomerByCurrentMonth()
{
     //gets some customer 
}

[HttpGet]
[Route("api/customers/{id}")]
public Customer GetCustomerById(string id)
{
  //gets a single customer by specified id
}
[HttpGet]
[Route("api/customers/customerByUsername/{username}")]
public Customer GetCustomerByUsername(string username)
{
    //gets customer by its username
}
Lalji Dhameliya
  • 1,729
  • 1
  • 17
  • 26
  • 5
    I think this answer is simple and straight forward even compatible with .NET Core. However repeated route prefixes can be avoided. See: https://stackoverflow.com/questions/12775590/routing-with-multiple-get-methods-in-asp-net-web-api/49070866#49070866 – Major Mar 02 '18 at 14:14
12

There are lots of good answers already for this question. However nowadays Route configuration is sort of "deprecated". The newer version of MVC (.NET Core) does not support it. So better to get use to it :)

So I agree with all the answers which uses Attribute style routing. But I keep noticing that everyone repeated the base part of the route (api/...). It is better to apply a [RoutePrefix] attribute on top of the Controller class and don't repeat the same string over and over again.

[RoutePrefix("api/customers")]
public class MyController : Controller
{
 [HttpGet]
 public List<Customer> Get()
 {
   //gets all customer logic
 }

 [HttpGet]
 [Route("currentMonth")]
 public List<Customer> GetCustomerByCurrentMonth()
 {
     //gets some customer 
 }

 [HttpGet]
 [Route("{id}")]
 public Customer GetCustomerById(string id)
 {
  //gets a single customer by specified id
 }
 [HttpGet]
 [Route("customerByUsername/{username}")]
 public Customer GetCustomerByUsername(string username)
 {
    //gets customer by its username
 }
}
Major
  • 5,948
  • 2
  • 45
  • 60
5

Only one route enough for this

config.Routes.MapHttpRoute("DefaultApiWithAction", "{controller}/{action}");

And need to specify attribute HttpGet or HttpPost in all actions.

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

[HttpGet]
public IEnumerable<object> TestGet2()
{
    return new string[] { "value3", "value4" };
}
Palanikumar
  • 6,940
  • 4
  • 40
  • 51
0

You might not need to make any change in the routing. Just add following four methods in your customersController.cs file:

public ActionResult Index()
{
}

public ActionResult currentMonth()
{
}

public ActionResult customerById(int id)
{
}


public ActionResult customerByUsername(string userName)
{
}

Put the relevant code in the method. With the default routing supplied, you should get appropriate action result from the controller based on the action and parameters for your given urls.

Modify your default route as:

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Api", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Joel
  • 7,401
  • 4
  • 52
  • 58
Murtuza Kabul
  • 6,438
  • 6
  • 27
  • 34
  • right. It is the controller which is responsible to render the given action results. To make the methods work like this, you will either have to rename it to customerController or change the route. If you want to keep the name of controller as apicontroller, change the route and put the default value for controller to "Api" instead of "Home". – Murtuza Kabul Oct 08 '12 at 05:17
  • yes I would like to keep the name as is, can you tell me how to write routing for this ? – Yasser Shaikh Oct 08 '12 at 05:24
  • I have edited the route, see how I have replaced the Default controller from "Home" to "Api" – Murtuza Kabul Oct 08 '12 at 05:53
  • I dont want an Index as an action in my controller – Yasser Shaikh Oct 08 '12 at 06:07
  • Index is for your default url "api/customers/". You do not have to put index in the url, it will automatically execute the Index action when no action is specified. See in the route, we have "Index" as default action. – Murtuza Kabul Oct 08 '12 at 06:09
  • I've seen the modified question and it will require more then one routes to accomplish the target. Further, the routes will become action specific. – Murtuza Kabul Oct 08 '12 at 06:14
  • Note: if this is an `ApiController` then you will need to use `routes.MapHttpRoute`, not `routes.MapRoute`. – Scotty.NET Dec 19 '13 at 16:20
0
  // this piece of code in the WebApiConfig.cs file or your custom bootstrap application class
  // define two types of routes 1. DefaultActionApi  and 2. DefaultApi as below

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

  // decorate the controller action method with [ActionName("Default")] which need to invoked with below url
  // http://localhost:XXXXX/api/Demo/ -- will invoke the Get method of Demo controller
  // http://localhost:XXXXX/api/Demo/GetAll -- will invoke the GetAll method of Demo controller
  // http://localhost:XXXXX/api/Demo/GetById -- will invoke the GetById method of Demo controller
  // http://localhost:57870/api/Demo/CustomGetDetails -- will invoke the CustomGetDetails method of Demo controller
  // http://localhost:57870/api/Demo/DemoGet -- will invoke the DemoGet method of Demo controller


 public class DemoController : ApiController
 {
    // Mark the method with ActionName  attribute (defined in MapRoutes) 
    [ActionName("Default")]
    public HttpResponseMessage Get()
    {
        return Request.CreateResponse(HttpStatusCode.OK, "Get Method");
    }

    public HttpResponseMessage GetAll()
    {
        return Request.CreateResponse(HttpStatusCode.OK, "GetAll Method");
    }

    public HttpResponseMessage GetById()
    {
        return Request.CreateResponse(HttpStatusCode.OK, "Getby Id Method");
    }

    //Custom Method name
    [HttpGet]
    public HttpResponseMessage DemoGet()
    {
        return Request.CreateResponse(HttpStatusCode.OK, "DemoGet Method");
    }

    //Custom Method name
    [HttpGet]
    public HttpResponseMessage CustomGetDetails()
    {
        return Request.CreateResponse(HttpStatusCode.OK, "CustomGetDetails Method");
    }
}
Vishwa G
  • 573
  • 1
  • 6
  • 13
0

I have two get methods with same or no parameters

[Route("api/ControllerName/FirstList")]
[HttpGet]
public IHttpActionResult FirstList()
{
}

[Route("api/ControllerName/SecondList")]
[HttpGet]
public IHttpActionResult SecondList()
{
}

Just define custom routes in AppStart=>WebApiConfig.cs => under register method

config.Routes.MapHttpRoute(
       name: "GetFirstList",
       routeTemplate: "api/Controllername/FirstList"          
       );
config.Routes.MapHttpRoute(
       name: "GetSecondList",
       routeTemplate: "api/Controllername/SecondList"          
       );
Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
Gaurav Dubey
  • 358
  • 4
  • 7
0

After reading lots of answers finally I figured out.

First, I added 3 different routes into WebApiConfig.cs

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

    // Web API routes
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "ApiById",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        constraints: new { id = @"^[0-9]+$" }
    );

    config.Routes.MapHttpRoute(
        name: "ApiByName",
        routeTemplate: "api/{controller}/{action}/{name}",
        defaults: null,
        constraints: new { name = @"^[a-z]+$" }
    );

    config.Routes.MapHttpRoute(
        name: "ApiByAction",
        routeTemplate: "api/{controller}/{action}",
        defaults: new { action = "Get" }
    );
}

Then, removed ActionName, Route, etc.. from the controller functions. So basically this is my controller;

// GET: api/Countries/5
[ResponseType(typeof(Countries))]
//[ActionName("CountryById")]
public async Task<IHttpActionResult> GetCountries(int id)
{
    Countries countries = await db.Countries.FindAsync(id);
    if (countries == null)
    {
        return NotFound();
    }

    return Ok(countries);
}

// GET: api/Countries/tur
//[ResponseType(typeof(Countries))]
////[Route("api/CountriesByName/{anyString}")]
////[ActionName("CountriesByName")]
//[HttpGet]
[ResponseType(typeof(Countries))]
//[ActionName("CountryByName")]
public async Task<IHttpActionResult> GetCountriesByName(string name)
{
    var countries = await db.Countries
            .Where(s=>s.Country.ToString().StartsWith(name))
            .ToListAsync();

    if (countries == null)
    {
        return NotFound();
    }

    return Ok(countries);
}

Now I am able to run with following url samples(with name and with id);

http://localhost:49787/api/Countries/GetCountriesByName/France

http://localhost:49787/api/Countries/1

Sam Salim
  • 2,145
  • 22
  • 18
-1
using Routing.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace Routing.Controllers
{
    public class StudentsController : ApiController
    {
        static List<Students> Lststudents =
              new List<Students>() { new Students { id=1, name="kim" },
           new Students { id=2, name="aman" },
            new Students { id=3, name="shikha" },
            new Students { id=4, name="ria" } };

        [HttpGet]
        public IEnumerable<Students> getlist()
        {
            return Lststudents;
        }

        [HttpGet]
        public Students getcurrentstudent(int id)
        {
            return Lststudents.FirstOrDefault(e => e.id == id);
        }
        [HttpGet]
        [Route("api/Students/{id}/course")]
        public IEnumerable<string> getcurrentCourse(int id)
        {
            if (id == 1)
                return new List<string>() { "emgili", "hindi", "pun" };
            if (id == 2)
                return new List<string>() { "math" };
            if (id == 3)
                return new List<string>() { "c#", "webapi" };
            else return new List<string>() { };
        }

        [HttpGet]
        [Route("api/students/{id}/{name}")]
        public IEnumerable<Students> getlist(int id, string name)
        { return Lststudents.Where(e => e.id == id && e.name == name).ToList(); }

        [HttpGet]
        public IEnumerable<string> getlistcourse(int id, string name)
        {
            if (id == 1 && name == "kim")
                return new List<string>() { "emgili", "hindi", "pun" };
            if (id == 2 && name == "aman")
                return new List<string>() { "math" };
            else return new List<string>() { "no data" };
        }
    }
}