modify the url which accesses my api - asp.net-web-api2

I've just finished creating my first api for a local project but I've come across another clause.
Currently my api can be accessed such as /api/{StartDate}/{EndDate}
So it can be navigated such as www.site.com/details/201001010000/201601010000
However, now I need it to be /api/details?StartDate={startDate}&EndDate={endDate}
Currently my code is this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{StartDate}/{EndDate}",
defaults: new { StartDate = RouteParameter.Optional, EndDate = RouteParameter.Optional }
);
[HttpGet]
[Route("")]
[Route("{StartDate}/{EndDate}")]
[ResponseType(typeof(Detail))]
public IHttpActionResult GetDetail(string StartDate, string EndDate)
{
DateTime StartDateTime = DateTime.ParseExact(StartDate, "yyyyMMddhhmm", null);
DateTime EndDateTime = DateTime.ParseExact(EndDate, "yyyyMMddhhmm", null);
IEnumerable<ConvertedDetails> detail = db.Details.Where(a => a.callDate >= StartDateTime && a.callDate <= EndDateTime).RestOfLongQuery;
if (detail.Any())
{
return Ok(ResponseTrue);
}
return Ok(ResponseFalse);
}

The problem lies in how you map the HTTP attribute routes and the Route attribute on your action. You can fix this by using the default http routes, which will prevent StartDate and EndDate from being part of the URL instead of parameters:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You also don't need the Route attribute on the controller, therefore it can look like this:
[HttpGet]
[ResponseType(typeof(Detail))]
public IHttpActionResult GetDetail(string StartDate, string EndDate)
{
DateTime StartDateTime = DateTime.ParseExact(StartDate, "yyyyMMddhhmm", null);
DateTime EndDateTime = DateTime.ParseExact(EndDate, "yyyyMMddhhmm", null);
IEnumerable<ConvertedDetails> detail = db.Details.Where(a => a.callDate >= StartDateTime && a.callDate <= EndDateTime).RestOfLongQuery;
if (detail.Any())
{
return Ok(ResponseTrue);
}
return Ok(ResponseFalse);
}
This will make it possible for you to use the following URL to access the action:
www.site.com/api/details?StartDate=201001010000&EndDate=201601010000

Related

mvc api does not reach Controller get method

I have a simple api written in MVC 4,when i run it write the address,it does not reach the controller:
here is my controller:
public IEnumerable<tenMinsStatcs> Get()
{
string id="192.168.39.32";
string dttimeFrom="05082019";
string dttimeTo="08082019";
string format = "ddMMyyyy";
DateTime fromdate = DateTime.ParseExact(dttimeFrom, format, CultureInfo.InvariantCulture);
DateTime todate = DateTime.ParseExact(dttimeTo, format, CultureInfo.InvariantCulture);
TestClasscs ts = new TestClasscs();
ts.m_turbine_id = IPAddress.Parse("192.168.39.82");
ts.m_time_stamp = Convert.ToDateTime("2019-08-07 5:20:30");
ts.m_wind_speed = 5;
ts.norm_wind_max = 3;
ts.norm_wind_min = 2;
ts.norm_wind_speed = 3;
ts.norm_wind_speed_without_ntf = 1;
List<TestClasscs> myTur = new List<TestClasscs>();
myTur.Add(ts);
// mm.m_time_stamp = Convert.ToDateTime("2019-08-07");
//// mm.m_turbine_id = "192.168.39.84";
// tst.Add(mm);
IPAddress turip = IPAddress.Parse(id);
// var rslt = _context.tenmins.Where(s => s.m_turbine_id ==turip && s.m_time_stamp >= DateTime.Now.AddDays(-1)).Take(2).ToList();
var rslt = (from m in _context.stat10
where m.m_turbine_id == turip && m.m_time_stamp >= fromdate && m.m_time_stamp <= todate
select new tenMinsStatcs
{
m_time_stamp = m.m_time_stamp,
// m_turbine_id = m.m_turbine_id.ToString(),
m_wind_speed = m.m_wind_speed
}).ToList();
return rslt;
}
here is my webApiConfig:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
any idea where im doing wrong?
when I run my code, after localhost i write api/Values ,considering this it should reach the controller right?
You need to change configuration in RouteConfig.cs file in which routes patterns as defined and ASP.Net MVC routes request as per below cofiguration. api literal prefix only in Web API not for MVC action method why you are appending api litteral after host name. ASP.Net MVC action method can be execute when you type URL- HTTP sheme://domain name:port/litteral/Controller Name/Action Name
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "api/{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional }
);
}

Using CacheCow to Cache Based On Parameter

I have a webapi endpoint that looks like the following in my Controller:
[HttpGet]
public IHttpActionResult GetPerson(
string term = null,
string workspace = null)
{
try
{
logger.Info("AvPerson start: " + DateTime.Now);
if (term == null)
{
return BadRequest();
}
ICoreAVData api = MvcApplication.Container.Resolve<ICoreAVData>();
List<Person> persons = new List<Person>();
persons.AddRange(api.GetAllPersonsForTerm(term, workspace));
if (persons == null)
{
return NotFound();
}
return Ok(persons);
}
catch (Exception)
{
return InternalServerError();
}
}
The term parameter can vary constantly but the workspace parameter displays what is relevant to the user. The user will not leave his own workspace, so that parameter will be constant from a user perspective.
I wonder if it is possible to have CacheCow cache based on the workspace parameter. ie. If workpace1 then cache it, if workspace2 then cache that separately.
I recognize that I will have to have add some kind of logic to invalidate that workspace specific cache. I'm not asking about that, because I believe I know how I might do that. I want to know if I can have a separate cache entry per workspace parameter.
Here is my routing setup for this controller:
config.Routes.MapHttpRoute(
name: "avperson",
routeTemplate: "api/v1/avperson/{action}/{id}",
defaults: new { controller = "avperson", id = RouteParameter.Optional }
);
Any ideas?
So the solution is to change the routing.
config.Routes.MapHttpRoute(
name: "avperson",
routeTemplate: "api/v1/{workspace}/avperson/{action}/{id}",
defaults: new { controller = "avperson", workspace = "all", id = RouteParameter.Optional }
);
Doing this will create a separate cached entry for each workspace which can then be validated or invalidated according to need.

custom method with same signature web api mvc4

I am using Web Api with ASP.NET MVC4. For Custom Get methods I am having problem, which I am explaining below.
my WebApiConfig.cs file is
public static void Register(HttpConfiguration config)
{
//1
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//2
config.Routes.MapHttpRoute(
name: "FindDetailsByAge",
routeTemplate: "api/{controller}/{action}/{age}",
defaults: new { age = RouteParameter.Optional },
constraints: new { age = #"^[0-9]+$" }
);
//3
config.Routes.MapHttpRoute(
name: "FindDetailsByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: new { name = RouteParameter.Optional },
constraints: new { name = #"^[a-z]+$" }
);
//4
config.Routes.MapHttpRoute(
name: "FindDetailsByCountry",
routeTemplate: "api/{controller}/{action}/{country}",
defaults: new { country = RouteParameter.Optional },
constraints: new { country = #"^[a-z]+$" }
);
// for json
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
}
Here is my controller file
public class ContactController : ApiController
{
private ContactRepository contactRepository;
public ContactController()
{
this.contactRepository = new ContactRepository();
}
public IEnumerable<Contact> GetAllContact()
{
return contactRepository.GetAll();
}
public Contact GetNameByAge(int id)
{
Contact contact = contactRepository.GetNameByAge(id);
if (contact == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return contact;
}
[HttpGet]
public Contact FindDetailsByAge(int age)
{
Contact contact = contactRepository.FindDetailsByAge(age);
if (contact == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return contact;
}
[HttpGet]
public IEnumerable<Contact> FindDetailsByName(string name)
{
IEnumerable<Contact> lstContactName = contactRepository.FindDetailsByName(name);
if (lstContactName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return lstContactName;
}
[HttpGet]
public IEnumerable<Contact> FindDetailsByCountry(string country)
{
IEnumerable<Contact> lstContactCountry = contactRepository.FindDetailsByCountry(country);
if (lstContactCountry == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return lstContactCountry;
}
}
When I am executing these methods my results are.
( I am entering the url in the firefox browser)
case - 1
input :
../api/contact/
output :
[{"Name":"Ashok","Age":60,"Country":"India"},{"Name":"Nargis","Age":30,"Country":"India"},{"Name":"Nargis","Age":35,"Country":"Iran"},{"Name":"Steve","Age":50,"Country":"South
Africa"}]
case - 2
input :
../api/contact/FindDetailsByAge/50
output :
{"Name":"Steve","Age":50,"Country":"South Africa"}
case - 3
input :
../api/contact/FindDetailsByName/nargis
output :
[{"Name":"Nargis","Age":30,"Country":"India"},{"Name":"Nargis","Age":35,"Country":"Iran"}]
case -4
input : ../api/contact/FindDetailsByCountry/india
output :
{"Message":"No HTTP resource was found that matches the request URI
'../api/contact/FindDetailsByCountry/india'.","MessageDetail":"No
action was found on the controller 'Contact' that matches the
request."}
From the above outputs you see that for case-4, it gives error.
FindDetailsByName is being executed
FindDetailsByCountry is not being executed
Now if I place the MapHttpRoute (4th case) before MapHttpRoute(3rd case) i.e. interchange the 3rd and 4th position, WebApiConfig.cs look like
...
...
//4
config.Routes.MapHttpRoute(
name: "FindDetailsByCountry",
routeTemplate: "api/{controller}/{action}/{country}",
defaults: new { country = RouteParameter.Optional },
constraints: new { country = #"^[a-z]+$" }
);
//3
config.Routes.MapHttpRoute(
name: "FindDetailsByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: new { name = RouteParameter.Optional },
constraints: new { name = #"^[a-z]+$" }
);
then output would be
input :
../api/contact/FindDetailsByCountry/india
output :
[{"Name":"Ashok","Age":60,"Country":"India"},{"Name":"Nargis","Age":30,"Country":"India"}]
input :
../api/contact/FindDetailsByName/nargis
output :
{"Message":"No HTTP resource was found that matches the request URI
'../api/contact/FindDetailsByName/nargis'.","MessageDetail":"No action
was found on the controller 'Contact' that matches the request."}
Now
FindDetailsByName is not being executed
FindDetailsByCountry is being executed
From the above code it is clear that in the config file for routing between 3 and 4, which one comes first is being executed.
I have
method -1
public Contact FindDetailsByName(string name)
method -2
public Contact FindDetailsByCountry(string country)
You see both the method FindDetailsByName and FindDetailsByCountry is taking only string parameter and returning Contact object.
Now my question is - How will I execute both the method ? What will be MapHttpRoute ? What will be MapHttpRoute's order ?
I have been searching the solution for last two days, but did not get any. I know I have given long description of the code, please read it patiently.
Thanks.
You should just need to have a single mapping that covers both. The api/{controller}/{action}/ covers the "api/contact/FindDetailsByName/" and "api/contact/FindDetailsByCountry".
With your current setup, the country one would take effect first, and the name one would be skipped because they have the same signature (controller/action/parameter).
Change your methods to accept a generic variable name, such as public IEnumerable<Contact> FindDetailsByName(string searchValue) and use a single route for both:
config.Routes.MapHttpRoute(
name: "FindDetailsBySearchValue",
routeTemplate: "api/{controller}/{action}/{searchValue}",
defaults: new { searchValue = RouteParameter.Optional },
constraints: new { searchValue = #"^[a-z]+$" }
);
OR don't use templates on the action, as in this answer: Web.API MapHttpRoute parameters
config.Routes.MapHttpRoute(
name: "FindDetailsByCountry",
routeTemplate: "api/{controller}/FindDetailsByCountry/{country}",
defaults: new { country = RouteParameter.Optional },
constraints: new { country = #"^[a-z]+$" }
);
//3
config.Routes.MapHttpRoute(
name: "FindDetailsByName",
routeTemplate: "api/{controller}/FindDetailsByName/{name}",
defaults: new { name = RouteParameter.Optional },
constraints: new { name = #"^[a-z]+$" }
);

MVC4 Web-API - Route mapping help required

i have a controller named
Products
and i have three actions in it
Product : takes an int returns a model(class) object
Category : takes a string returns array of model(class) object
All : no parameters return array of model(class) object
what i was trying was the following mapping
Product -> api / products / product / 1
Category -> api / product / category / sauces
All -> api / product / all
as the names of action support the URL structure so i was trying a general route which is
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
all else is working but i am getting the following error when i use this http://localhost:2271/api/products/category/2 URL
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:2271/api/products/category/2'.
</Message>
<MessageDetail>
No action was found on the controller 'Products' that matches the request.
</MessageDetail>
</Error>
on the other hand this URL api/products/category/?cat=abc & api/products/category?cat=abc is working fiine...... [cat is my receiving parameter name]
help !!!
on the other hand this URL api/products/category/?cat=abc &
api/products/category?cat=abc is working fiine
Yes that's right, it will, and that's the RPC style of doing it. If you want to maintain the RESTful way of doing it you can have the following route configuration:
config.Routes.MapHttpRoute(
name: "ProductById",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ProductByCategory",
routeTemplate: "api/{controller}/category/{cat}"
);
config.Routes.MapHttpRoute(
name: "DefaultByAction",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "Get" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And your controller will look something like this:
public class ProductsController : ApiController
{
// api/products/
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// api/products/5
[HttpGet]
public string Get(int id)
{
return "Product" + id;
}
// api/products/category/fruits
[HttpGet]
public string Category(string cat)
{
return "Product " + cat;
}
}
NOTE: I returned strings for simplicity's sake but I assume you will return an instance of a Product, and you can easily change the code to do so.

Multiple HttpRoute in ASP.NET MVC 4

I'm trying to make a RouteConfig in Web API, that allows following patterns:
Patterns:
/api/{controller}
/api/{controller}/{id} (int, optional)
/api/{controller}/{action}
/api/{controller}/{action}/{id} (int, optional)
Use cases:
/api/profile/ (get all profiles)
/api/profile/13 (get profile number 13)
/api/profile/sendemail/ (send email to all profiles)
/api/profile/sendmail/13 (send email to profile number 13)
What I'm trying is the following:
routes.MapHttpRoute(
name: "ControllerAndID",
routeTemplate: "api/{controller}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" } // Dekkar heiltölur eingöngu í id parameter
);
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "Get", id = RouteParameter.Optional }
);
The error I'm getting is:
Multiple actions were found that match the request:
\r\nMinarSidur.Models.DataTransfer.UserProfileDTO sendmail(System.String)
on type
MinarSidur.Controllers.ProfileController\r\nMinarSidur.Models.DataTransfer.UserProfileDTO
sendpaycheck(System.String) on type MinarSidur.Controllers.ProfileController
Can you help me accomplishing this?
Your exception was actually complaining about their being a conflict between these two methods on the Profile controller:
sendpaycheck(string)
sendmail(string)
Not the Get and Get(?); although this would also be an issue.
Really, when carrying out RPC actions that make changes or trigger actions you should use the POST verb. By doing this your routing issues mentioned above should be resolved.
Updated
Have you considered a more resource centric approach to your problem? In all cases here the resource is "Profile" and it appears to have a unique id of x. It appears to also have two other possible unique id's email and ssn?
If these were acceptable URL's to you
http://localhost/api/profile
http://localhost/api/profile/x
http://localhost/api/profile/?email=myemail#x.com
http://localhost/api/profile/?ssn=x
you could use:
public class ProfileController : ApiController
{
public string Get(int id)
{
return string.Format("http://localhost/api/profile/{0}", id);
}
public string Get([FromUri] string email = null, [FromUri] int? ssn = null)
{
if (!string.IsNullOrEmpty(email))
{
return string.Format("http://localhost/api/profile/?email={0}", email);
}
if (ssn.HasValue)
{
return string.Format("http://localhost/api/profile/?ssn={0}", ssn.Value);
}
return "http://localhost/api/profile";
}
}
With just the standard webapi routing:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
But
If you did want to carry on with /email and /ssn you may have issues with the email... specifically with the "." in the email address and this can confuse the routing engine... for this to work you must put a trailing slash i.e. http://localhost/api/profile/email/me#me.com/ I think you will find http://localhost/api/profile/email/me#me.com wont work.
This supports:
http://localhost/api/profile
http://localhost/api/profile/x
http://localhost/api/profile/email/myemail#x.com/
http://localhost/api/profile/ssn/x
I would try this and use (NB. the use of the name rpcId to differentiate the routes):
public class ProfileController : ApiController
{
public string Get(int id)
{
return string.Format("http://localhost/api/profile/{0}", id);
}
public string Get()
{
return "http://localhost/api/profile";
}
[HttpGet]
public string Ssn(int rpcId)
{
return string.Format("http://localhost/api/profile/ssn/{0}", rpcId);
}
[HttpGet]
public string Email(string rpcId)
{
return string.Format("http://localhost/api/profile/email/{0}", rpcId);
}
}
My routing would then be:
config.Routes.MapHttpRoute(
name: "ProfileRestApi",
routeTemplate: "api/profile/{id}",
defaults: new { id = RouteParameter.Optional, Controller = "Profile" }
);
config.Routes.MapHttpRoute(
name: "PrfileRpcApi",
routeTemplate: "api/profile/{action}/{rpcId}",
defaults: new { Controller = "Profile" }
);
The rules overlap. Would the validation of the Id in routing be a great loss? You could still have the Id parameter as an int within your Get actions. If that's the case I think you can remove ControllerAndID entirely and just use the second which will match all your use cases.