web api routing with action and id - c#

I am new in using web api and I am trying to call a specific method in my controller.
I have
global.asax
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
the WebApiConfig class with these routings
// Web API routes
config.MapHttpAttributeRoutes();
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
{
action="DefaultAction",
id = RouteParameter.Optional
}
);
and my controller
[HttpGet]
public HttpResponseMessage GetPatSummary(string PatId)
{
PatientSummary Pat = new PatientSummary();
HttpResponseMessage Response = new HttpResponseMessage();
string yourJson = Pat.GetPatient(PatId);
Response = this.Request.CreateResponse(HttpStatusCode.OK, yourJson);
return Response;
}
[ActionName("DefaultAction")] //Map Action and you can name your method with any text
public IHttpActionResult GetPatient(int id)
{
Object Obj = new object();
if (Obj!=null)
{
return NotFound();
}
return Ok(Obj);
}
the URL I am using is
http://localhost/mdmwapi/api/MdmwPatientController/GetPatSummary/sgdgdgddhdhd1334254
but I get this error
A path segment cannot contain two consecutive parameters. They must be separated by a '/' or by a literal string.
I am getting nut :-)

Use attribute routing
[HttpGet]
[Route("api/MdmwPatientController/GetPatSummary/{PatId}")]
public HttpResponseMessage GetPatSummary(string PatId)
{
PatientSummary Pat = new PatientSummary();
HttpResponseMessage Response = new HttpResponseMessage();
string yourJson = Pat.GetPatient(PatId);
Response = this.Request.CreateResponse(HttpStatusCode.OK, yourJson);
return Response;
}
then you can request it using
http://localhost/api/MdmwPatientController/GetPatSummary/yourpatid
also you can map any url using attribute routing this way

the solution is a combination of a new route and a mistake in the URL
the new routes are now these ones
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
config.Routes.MapHttpRoute(
name: "ApiMethodCall",
routeTemplate: "api/{controller}/{action}/{PatId}",
defaults: new
{
controller= "MdmwPatient",
action= "GetPatSummary"
}
);
and the error in the URL was that although the controller class name is MdmwPatientController I have to omit the "controller" suffix when calling from the test client, so the correct url is
http://localhost/mdmwapi/api/MdmwPatient/GetPatSummary/sgdgdgddhdhd1334254

Related

how to set routing for unusual endpoint api configuration?

I am building a new webapi for use at work. Where I have to develop a webapi2 application that fits the following endpoint schema
/file
/file/[data id]
/file/[data id]/documents
/file/[data id]/conditions
In my controller I have the following code:
public class FileController : ApiController
{
[HttpPost]
public HttpResponseMessage ImportFile()
{
var act = Request.Headers.Accept.ToString();
// test content type for "application/vnd.exp"
return Request.CreateResponse(HttpStatusCode.OK, $"Successful import # {DateTime.Now}");
}
[HttpPatch]
public HttpResponseMessage UpdateDataByFile(string dataId)
{
var act = Request.Headers.Accept.ToString();
return Request.CreateResponse(HttpStatusCode.OK, "Successful save");
}
[HttpPatch]
public HttpResponseMessage UpdateDataIntake(string dataId)
{
var act = Request.Headers.Accept.ToString();
return Request.CreateResponse(HttpStatusCode.OK, "Successful save");
}
[HttpGet]
public HttpResponseMessage GetDataConditionsForUser(string dataid)
{
var act = Request.Headers.Accept.ToString();
return Request.CreateResponse(HttpStatusCode.OK, "Successful get");
}
}
My route config looks like the following:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "Files1",
routeTemplate: "{controller}/{action}"
);
routes.MapHttpRoute(
name: "Files2",
routeTemplate: "{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
}
}
My question is how do I configure the routing so that it matches the endpoint configuration
eg: "https://something.com/file/123412/document"
and have it hit the correct controller method?
Really confused about how to set routing in an environment like this.
You can do something like this-
routes.MapHttpRoute(
name: "File",
routeTemplate: "file",
defaults: new { controller = "File", action = "ImportFile" }
);
routes.MapHttpRoute(
name: "FileUpdate",
routeTemplate: "file/{dataId}",
defaults: new { controller = "File", action = "UpdateDataByFile" }
);
routes.MapHttpRoute(
name: "FileDocuments",
routeTemplate: "file/{dataId}/documents",
defaults: new { controller = "File", action = "UpdateDataIntake" }
);
routes.MapHttpRoute(
name: "FileConditions",
routeTemplate: "file/{dataId}/conditions",
defaults: new { controller = "File", action = "GetDataConditionsForUser" }
);
routes.MapHttpRoute(
name: "Files1",
routeTemplate: "{controller}/{action}"
);
routes.MapHttpRoute(
name: "Files2",
routeTemplate: "{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
That means you are using a custom route for each Action. You are only using these routes for urls that start like 'file/'. You have a lot of different ways that you can do this.
If you use {controller} in these templates and remove default: controller = "File" then you will match for other controllers.
If you can rename your Controller Actions to match your routing you may be able to fit them into a pattern, and use the {action} in the template.
routes.MapHttpRoute(
name: "File",
routeTemplate: "{controller}/{dataId}/{action}",
defaults: new {}
);
If you rename your last two Actions this should match them.
public class FileController : ApiController
{
[HttpPatch]
public HttpResponseMessage Documents(string dataId)
{
var act = Request.Headers.Accept.ToString();
return Request.CreateResponse(HttpStatusCode.OK, "Successful save");
}
[HttpGet]
public HttpResponseMessage Conditions(string dataid)
{
var act = Request.Headers.Accept.ToString();
return Request.CreateResponse(HttpStatusCode.OK, "Successful get");
}
}
And if you set a default action in that route, you can also match your second Action
routes.MapHttpRoute(
name: "File",
routeTemplate: "{controller}/{dataId}/{action}",
defaults: new { action = "UpdateDataByFile" }
);

Multiple POST-request in web api

I need to use multiple POST-requests in web API but get an error: "Multiple actions were found that match the request..."
I have 2 POST-requests in my controller:
public void PostStart([FromBody]string value)
{
CookieHeaderValue cookie = Request.Headers.GetCookies("user").FirstOrDefault();
...
}
public void PostLogin([FromBody]string value)
{
CookieHeaderValue cookie = Request.Headers.GetCookies("user").FirstOrDefault();
...
}
My route file looks like this currently:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "apistart",
routeTemplate: "Home/api/values/start/{id}",
defaults: new { action = "PostStart", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "apilogin",
routeTemplate: "Home/api/values/login/{id}",
defaults: new { action = "PostLogin", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
If I remove on of the requests from the controller, everything works fine, so my routes seem valid, but when both of requests are present, router can't find the right one.
Any thoughts?
I've tried alredy to use another default route but it changes nothing:
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You can use [HttpPost] attribute to specify request method:
[HttpPost]
public void Start([FromBody]string value)
{
CookieHeaderValue cookie = Request.Headers.GetCookies("user").FirstOrDefault();
...
}
[HttpPost]
public void Login([FromBody]string value)
{
CookieHeaderValue cookie = Request.Headers.GetCookies("user").FirstOrDefault();
...
}
That will allows you to use as many post actions as you want with using default action-based route rule.
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You should use RouteAttribute to make it work:
[Route("start")]
public void PostStart([FromBody]string value)
{
CookieHeaderValue cookie = Request.Headers.GetCookies("user").FirstOrDefault();
...
}
[Route("login")]
public void PostLogin([FromBody]string value)
{
CookieHeaderValue cookie = Request.Headers.GetCookies("user").FirstOrDefault();
...
}
WebApi doesn't take into consideration method name, only first word to resolve http method. Thats why you have error which says about "Multiple actions..." - there are two actions which can handle POST request.

How define a WebApi route to access method

I'd like have access to a method other than "GET", "PUSH", "PATCH", ....
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Employee",
routeTemplate: "api/employee/{employeeid}",
defaults: new { controller = "employee", employeeid = RouteParameter.Optional }
);
//for test : not work
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
//JSON Formatting
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
I have access to the employee controller :
[RoutePrefix("api/employee")]
public class EmployeeController : ApiController
{
public HttpResponseMessage Get() { }
public HttpResponseMessage Get(int employeeid) {}
public HttpResponseMessage Post([FromBody] EmployeeModel model){}
[HttpPut]
[HttpPatch]
public HttpResponseMessage Patch([FromBody] EmployeeModel model){}
[Route("initialisation")]
public HttpResponseMessage Initialisation() {}
}
I have access without any problem :
http://localhost/employee
http://localhost/employee/1
I'd like have access to the "Initialisation" method :
http://localhost/employee/initialisation
I added the route "DefaultApi" but when I try I get this error :
{
"$id": "1",
"message": "The request is invalid.",
"messageDetail": "The parameters dictionary contains a null entry for parameter 'employeeid' of non-nullable type 'System.Int32'
for method 'System.Net.Http.HttpResponseMessage Get(Int32)' in 'Pme.WebApi.Controllers.EmployeeController'.
An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
Thanks,
Try the following:
Change
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
To
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{employeeid}",
defaults: new { action = "get", employeeid= RouteParameter.Optional }
);
The only change the name of last ID parameter from "id" to "employeeid"
I think this should work out. You can try modifying your WebApiConfig like this :
config.Routes.MapHttpRoute(
name: "DefaultApi",
//routeTemplate: "api/{controller}/{id}",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
This way, you keep it open for both, the controller name and the action name to be something dynamic and try hitting the URL that would be generated as per this format.
UPDATE :
Comment the "Employee" route code. Just keep the "DefaultApi" route active. Now try to hit this URL :
http://localhost:1955/api/Employee/Create/parameters
You obviously need to keep the host name as per yours and the parameter too.
Hope this helps.
You need to register attribute routing in your WebApiConfig
config.MapHttpAttributeRoutes();
See Attribute Routing in ASP.NET Web API 2
Alternatively you can specify exactly where your route should go
config.Routes.MapHttpRoute("Initialisation", "api/employee/initialisation",
new {controller = "employee", action = "initialisation"});

ASP.NET Web Api url design

I try to use Web Api in an ASP.NET MVC4 application.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionIdApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
public class OrganisationApiController : ApiController
{
public List<Direction> GetDirections()
{
List<Direction> res = new List<Direction>();
using (SerializerContext context = new SerializerContext())
{
res = context.DirectionSet.ToList();
}
return res;
}
public List<Departement> GetDepartements(int directionId)
{
List<Departement> res = new List<Departement>();
using (SerializerContext context = new SerializerContext())
{
res = context.DepartementSet.Where(d => d.IdDirection == directionId).ToList();
}
return res;
}
}
Application starts with
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
When I develop I can call all my methods from OrganisationApiController :
http://localhost:5000/api/OrganisationApi => GetDirections is called
http://localhost:5000/api/OrganisationApi/GetDirections => GetDirections is called
http://localhost:5000/api/OrganisationApi/GetDepartements/1 => GetDepartement is called with directionId = 1
But when I deploy on a server with IIS
http://myserver.com:5000/api/OrganisationApi => GetDirections is called
http://myserver.com:5000/api/OrganisationApi/GetDirections => GetDirections is called
http://myserver.com:5000/api/OrganisationApi/GetDepartements/1 => 404 error
What I am missing?
You have a problem with those routes:
config.Routes.MapHttpRoute(
name: "ActionIdApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);
The second route is unnecessary (because the first one already handles those requests - id is specified as optional) and it has an error that you specifying that id is optional but the route doesn't contain an id at all.
Either remove the second route and change your action so it receive an optional parameter or replace them with:
config.Routes.MapHttpRoute(
name: "ActionIdApi",
routeTemplate: "api/{controller}/{action}/{id}",
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}",
);
By the way your default rote is also very problematic because you are not specifying default controller and action - it supposed to be DEFAULT right...
From what i see here all your routes could be replaced with this one
config.Routes.MapHttpRoute(
name: "ApiDefault",
routeTemplate: "api/{controller}/{action}/{id}",
new { controller = "HomeApi", action="IndexApi" id = RouteParameter.Optional });
Just replace your default values for controller and action.
EDIT:
The problem is in your action:
If you specify in your Route "id" parameter your action should be:
public List<Departement> GetDepartements(int id)
Parameter names must match.
Also you can combine both your actions into one action:
public List<Departement> GetDepartements(int? id)
{
List<Departement> res = new List<Departement>();
using (SerializerContext context = new SerializerContext())
{
if(id.HasValue)
{
res = context.DepartementSet.Where(d => d.IdDirection == directionId).ToList();
}
else
{
res = context.DirectionSet.ToList();
}
}
return res;
}

WebApi Query String is stripped from RequestURI

I have a WebApi Get action
public HttpResponseMessage Get()
{
try
{
var queryValue = Request.RequestUri.ParseQueryString();
if (queryValue.Count == 0)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "Query String Filters Required");
}
I call with with this url
api/funds?FundProductGroupCT=favourite&pagesize=10&startindex=8
RequestUri always has the query string stripped.
this is my global.asax
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
System.Web.Http.GlobalConfiguration.Configuration.Routes.MapHttpRoute
("default", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = System.Web.Http.RouteParameter.Optional });
Apologies.
This turned out to be a bit of a red herring. I'm using the web api with in an Ektron app. I've found there is a module which intercepts the request and if it doesnt end with a "/" strips the query string.

Categories

Resources