I'm trying to add attribute routing to a ApiController like so:
public class PhoneNumbersController : ApiController
{
// GET BY ID
public PhoneNumber GetById(int id)
{
return PhoneNumbersSelect(id)[0];
}
// GET BY ID TypeOFPhoneNumbers/Id
[Route("api/typeOfPhoneNumbers/{id}")]
public TypeOfPhoneNumber GetTypeOfPhoneNumberById(int id)
{
return TypeOfPhoneNumbersSelect(id)[0];
}
}
My config looks like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I'm getting a 500 Error when trying to call api/phoneNumbers/1. If I comment out the GetTypeOfPhoneNumbersId() function the default controller GetById() works fine. What am I doing wrong? Am I not allowed to declare a unique route in the ApiController because of the way the config is set up?
Also, as I just found out, calling the api/typeOfPhoneNumbers/1 returns a 404 error, so that routing doesn't work at all.
Thanks for any help!
I believe you miss the controller name (phoneNumbers) in your route, this is a working code (I've tested it)
public class PhoneNumbersController : ApiController
{
// GET BY ID
[HttpGet]
public PhoneNumber GetById(int id)
{
return PhoneNumbersSelect(id)[0];
}
// GET BY ID TypeOFPhoneNumbers/Id
[HttpGet]
[Route("api/phoneNumbers/typeOfPhoneNumbers/{id:int}")]
public TypeOfPhoneNumber GetTypeOfPhoneNumberById(int id)
{
return TypeOfPhoneNumbersSelect(id)[0];
}
}
Could you try to access TypeOFPhoneNumbers's resource like that : ~api/typeOfPhoneNumbers?id=1
Related
i am new to webapi and MVC in general. If I wanted to group my service URLs like this
/api/account/create
/api/account/login
/api/account/resetpass
Am I able to put all 3 method calls in the same controller file and somehow map a particular request to the right method?
Create a Controller named Account and Create 3 [GET, POST, PUT, DELETE] method and name them create , login ,resetpass.
By Default, this is the routing for MVC / API(Id can be optional)
route Template: "api/{controller}/{id}",
Example :
public class AccountController : ApiController
{
[HttpPost]
public string Create()
{
// CODE
}
[HttpPost] // or [HttpGet]
public string Login ()
{
// CODE
}
[HttpPost]
public string Resetpass()
{
// CODE
}
}
if you had trouble calling them, try to give them a specific route :
[HttpGet("GetSubject/{subject}")]
public int GetSubjectId(String subject)
{
//CODE
}
Please if you get any error or misunderstanding, don't hesitate to post a comment
I have a GET method without parameter and want below to work
/api/books.xml
This however works with forward slash
/api/books/.xml
[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
[HttpGet]
[Route(".{format}")]
[FormatFilter]
public ActionResult<List<Book>> Get()
{
return bookService.Get();
}
}
Possible solutions that I tried are
Annotating without {id}
[Route("[controller]/[action].{format}")] // no slash between [action] and .{format}
Adding a default route in Startup.cs without {id}, so that if id parameter is not passed like in this problem then the routing should not expect a slash after {action}.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}");
});
Based on the currently defined routes on the controller, what you describe is by design.
Consider changing the routes to match the desired URL format
[ApiController]
public class BooksController : ControllerBase {
[HttpGet]
[Route("api/[controller].{format}")] //<--- GET api/books.xml
[FormatFilter]
public ActionResult<List<Book>> Get() {
return bookService.Get();
}
}
Hi I've 2 different get methods with the below signature and my routing is as follows:
[ActionName("Data")]
public Dictionary<int, string> GetData(int ID)
{
}
[ActionName("Name")]
public Dictionary<int, string> GetName(int ID)
{
}
var route = routes.MapRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But api/{controller}/Data/9 and api/{controller}/Name/10 are both calling the first method GetData.
Can someone please guide me to fix this.
You will have to decorate your controllerclass with the [RoutePrefix("api/ControllerName")] as well as do the following:
[Route("Data")]
public Dictionary<int, string> GetData(int ID)
{
}
[Route("Name")]
public Dictionary<int, string> GetName(int ID)
{
}
var route = routes.MapRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You can get a little more fancy with the Route attribute.
[Route("Name/{ThisIsMyIdVariableName}"), HttpGet]
public HttpResponseMessage GetName(int ThisIsMyIdVariableName) { /*code here*/ }
Now you can keep the idea of a pretty url without having to use routes.MapRoute and can call the url like domain.com/Name/19. You can use multiple variable names wrapped with {} in the route and keep your urls pretty and restful when passing multiple variables.
This approach is also very useful if you have the need to version your api controller.
I know this should be a comment instead of an answer, but I don't have the required reputation to comment yet, and I'd like to help. Are both of the above methods returning data based on their id (PK) value? The reason both methods are calling the first method in your Web API server is because they're both appending the same URL extension to the server. A quick and easy way to differentiate these methods could be to change one of the 2 to accept a string that you could then convert back to an int. For instance:
//api/Customer/5
public Dictionary<int, string> GetData(int ID)
{
}
//api/Customer?CustID={custID]
public Dictionary<int, string> GetName(string custID)
{
//convert string to int and continue code
}
This might not be best practice, but this is what I've done in the past when needing to differentiate between GET/POST methods in my web api server. For deserializing, you could use a class like JsonConvert if you're using Json, etc. Then your calls to your controller will add the appropriate URL extensions as defined in the comments I left above the methods. Let me know if you'd like additional explanation.
In my WebApiConfig.cs file I have the following route defined: (it's the only route defined here)
config.Routes.MapHttpRoute(
name: "DefaultApi2",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
The POST I'm making is to:
http://localhost:17138/api/Account/Login
My controller is this:
namespace WebAPI.Api
{
public class AccountController : ApiController
{
[HttpPost]
public HttpResponseMessage Login(string username,string password)
{...
The error I'm getting is:
{"Message":"NoHTTPresourcewasfoundthatmatchestherequestURI'http://localhost:17138/api/Account/Login'.","MessageDetail":"Noactionwasfoundonthecontroller'Account'thatmatchestherequest."}
and from Phil's program it looks like the route should work. Any thoughts?
I changed my parameters for my Login method to
public HttpResponseMessage Login(FormDataCollection formDataCollection)
and it worked. It seems I'm missing something about how POST parameters are handled because in the formDataCollection, both username and password are there. I'm not sure what that is not the same as
public HttpResponseMessage Login(string username,string password)
I have a controller action as the below
public class HomeController : BaseController
{
public JsonResult Index(ComplexObject customObject)
{
...
}
...
}
This is what ComplexObject looks like
public class ComplexObject
{
public int? Id { get; set; }
...
}
Here is what I have defined in my area registration:
context.MapRoute(
"MyArea_default",
"MyArea/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "MyApp.Areas.MyArea.Controllers" }
);
What I am trying to do is access my controller action like
https://mysite.com/MyArea/{value_of_id}
and have my modelbinder for ComplexObject initialize a new ComplexObject with the id that was passed in.
Is this possible? An internet search offered no help at all.
Many thanks in advance for your help!
Yes, mvc will bind a route parameter to a complex obejct. Have you actually tried it?