web api route with optional parameter - c#

I am currently playing around with some things...According to this link, I need to construct a route that is open to the following format
webServiceURL/version/devices/deviceLibraryIdentifier/registrations/passTypeIdentifier?passesUpdatedSince=tag
so I defined the route like so
config.Routes.MapHttpRoute(
name: "DefaultApi3",
routeTemplate: "{version}/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{passesUpdatedSince}",
defaults: new { controller = "SerialNumbers", action = "GET", passesUpdatedSince = RouteParameter.Optional }
);
However, the following route fails for the url
http://localhost/v1/devices/24358235235loji200/registrations/pass.com.mypass?passesUpdatedSince=12a512
How can I configure the route so that the above url can reach my controller?
My controller looks like
[HttpGet]
public HttpResponseMessage Get(string passesUpdatedSince ="")
{
//do stuff
}
UPDATE
Thanks to the comments, I've made the following changes.
the route
config.Routes.MapHttpRoute(
name: "DefaultApi3",
routeTemplate: "v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}",
defaults: new { controller = "SerialNumbers", action = "GET" }
);
My controller is as follows
public HttpResponseMessage Get(string deviceLibraryIdentifier,
string passTypeIdentifier,
string passesUpdatedSince = "")
{
//do stuff
}
According to the Apple docs, is it right to assume the following the webservice calls could look like
http://localhost:31472/v1/devices/23lk5235232oijlk/registrations/pass.com.mypass
http://localhost:31472/v1/devices/23lk5235232oijlk/registrations/pass.com.mypass?passesUpdatedSince=159025
as these are returning 404.
These, however, do work.
http://localhost:31472/v1/devices/23lk5235232oijlk/registrations/pass.com.mypass/?passesUpdatedSince=1415l
http://localhost:31472/v1/devices/23lk5235232oijlk/registrations/pass.com.mypass/
So would there be a way to get it to work without the presence of the / near the end of the url?
It does look like the device is unable to recognize the route. I get the following message
Get serial #s task (for device 2523ff2fswtsfdh6544, pass type pass.com.mypass, last updated (null); with web service url https://weburl) encountered error: Unexpected response code 404

Because part of the URI had periods in it (pass.com.mypass), this always returned a 404
I had to add the
<modules runAllManagedModulesForAllRequests="true" />
in my web.config. And after that, everything worked as expected

For the route, try:
config.Routes.MapHttpRoute(
name: "DefaultApi3",
routeTemplate: "{version}/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}",
defaults: new { controller = "SerialNumbers", action = "GET" }
);
Note that you should actually have a hard-coded value where {version} is, according to the link you gave us (https://developer.apple.com/library/ios/documentation/PassKit/Reference/PassKit_WebService/WebService.html#//apple_ref/doc/uid/TP40011988-CH0-SW4).
A hard-coded version would look like this:
config.Routes.MapHttpRoute(
name: "DefaultApi3",
routeTemplate: "v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}",
defaults: new { controller = "SerialNumbers", action = "GET" }
);
Your controller action also needs to be able to accept all parameters of the route:
[HttpGet]
public HttpResponseMessage Get(string deviceLibraryIdentifier,
string passTypeIdentifier,
string passesUpdatedSince ="")
{
//do stuff
}

Related

My WebAPI route keeps returning a 404 error when containing a period

I have been having issues setting up my custom WebApi route in C#. I am setting up a custom route that uses a company name a parameter. The route works fine, unless the company name ends with a "." I have tried setting double escape but keep getting a 404 error.
Route
config.Routes.MapHttpRoute(
name: "Contracts",
routeTemplate:"{company}/Contracts/{action}/{id}",
defaults: new {controller= "Contracts", id= RouteParameter.Optional}
);
Controller
public class ContractsController : ApiController
{
[HttpGet, ActionName("PrivacyStatements")]
public HttpResponseMessage GetPrivacyStatements(string Company)
{
//code for privacy statements
}
These will work:
http://localhost/Company%20Name/Contracts/PrivacyStatements
http://localhost/Company.%20Name/Contracts/PrivacyStatements
http://localhost/Contracts/PrivacyStatements?Company=Company%20Name%20Inc.
This does not work:
http://localhost/Company%20Name%20Inc./Contracts/PrivacyStatements
the "." before the "/" in "/Company Name Inc./" always gives me a 404.
Any help is appreciated.

MVC routing parameters not working at all

I've looked for a solution to this but even the simplest examples aren't working properly. Passing a single parameter {id} works successfully but that's the only parameter that is working. Changing the single parameter to anything else fails. In the example below multiple parameters also fail. It seems as the only workable parameter is "id".
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Servers",
url: "{controller}/{action}/{id}/{a}",
defaults: new
{
controller = "Test"
}
);
}
public class TestController : Controller
{
[HttpGet]
public ActionResult Monster(string id, string a)
{
return Json(new { success = id }, JsonRequestBehavior.AllowGet);
}
}
The url localhost/Test/Monster/hi Successfully reads the parameter as "hi". Specifying localhost/Test/Monster/hi/hello fails and gives a 404.
Try this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Servers",
url: "{controller}/{action}/{id}/{a}",
defaults: new
{
controller = "Test",
id = UrlParameter.Optional,
a = UrlParameter.Optional
}
);
}
Also, is this your only route?
The order that the routes are set-up in is important, it's very easy to overwrite a route with a later route. I have done that mistake countless times.
In case action is not optional, you should specific the default value for it. Please try :
routes.MapRoute(
name: "Servers",
url: "{controller}/{action}/{id}/{a}",
defaults: new
{
controller = "Test",
action = "Monster"
}
);
In your method you have specified the parameter string a so when you pass the URl localhost/Test/Monster/hi/hello MVC will look for the parameter a in the url as it matches the form post parameters with the parameters in the function
So this link might help you as it helped me
http://www.codeproject.com/Articles/299531/Custom-routes-for-MVC-Application
This is a very late response but the issue with this was that there was an area being registered further downstream that was causing the routing issues. The area being registered had an optional url parameter that was taking the routes over. Utilizing this registered area fixed the issue.
sorry to say but as you saying
localhost/Test/Monster/Hi
working mean only one parameter routing is configured... did you try by restarting the IISExpress as routing get loaded on very first call and one time only..
after making the changes in routing you have to stop the IIS Express from the Icon Tray and re-run you project then with one parameter it should throw error.. as you have not set these option it will work only when u specify both the parameters.

Restful post throws 400 invalid url when reaching 295 chars

I've created a restful webservice with Web-Api.
I'm trying to do a post at this url
../api/AAEAAAD_____AQAAAAAAAAAMAgAAAEVPYmplY3RUb0Jhc2U2NCwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAABlPYmplY3RUb0Jhc2U2NC5DcmVkZW50aWFsAgAAABk8VXNlcm5hbWU-a19fQmFja2luZ0ZpZWxkGTxQYXNRmllbGQBAQIAAAAGAwAAAA5hd2NhQGF0ZWEtYW5jdAYEAAAAC0czcnRtNG5zMGZ0CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2/say
The base64 is encoded with
HttpServerUtility.UrlTokenEncode();
I get a "HTTP Error 400. The request URL is invalid." when trying to do a post.
I've tried setting maxUrlLength as I've seen a few others with the same type of problem, alas, this did not help.
So far, I've tried
changing maxUrlLength in web.config.
Setting UrlSegmentMaxLength in Registry
nothing has worked so far.
I've found the magic number to be 294 allowed chars in the full url meaning -> If I cut of some of the characters from the long string until i get to 294 characters, everything works as intented, as to why certain it's not a routing problem nor a problem with my post method
Any good ideas as to what can be the issue?
For anyone trying to achieve the same thing I'm trying -
Heres my route
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{key}/{controller}/{id}",
defaults: new { key=RouteParameter.Optional,id = RouteParameter.Optional }
);
and my Post method
public string Post(string key)
{
if(ConvertFromBase64(key))
{
//Do stuff
}
}
Try with the key in the query string instead?
public class SomeController : Controller
[Route("api/say")]
public ActionResult Say(string key) {
}
With a url like
../api/say?key=AAEAAAD_____AQAAAAAAAAAMAgAAAEVPYmplY3RUb0Jhc2U2NCwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAABlPYmplY3RUb0Jhc2U2NC5DcmVkZW50aWFsAgAAABk8VXNlcm5hbWU-a19fQmFja2luZ0ZpZWxkGTxQYXNRmllbGQBAQIAAAAGAwAAAA5hd2NhQGF0ZWEtYW5jdAYEAAAAC0czcnRtNG5zMGZ0CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2
I know you tried through the web.config file, but can you try to increase the maxLength of the key parameter, like such ?
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{key:maxlength(500)}/{controller}/{id}", //whatever is the max length of your parameter...
defaults: new { key=RouteParameter.Optional,id = RouteParameter.Optional }
);

WebAPI accepts broken JSON send via Fiddler

For some reason I can send these two JSON requests to my WebAPI and it'll accept and create an User. One with double quotes and one without.
{"Username":"Bob", "FirstName":"Foo", "LastName":"Bar", "Password":"123", "Headline":"Tuna"}
{Username:"Bob", FirstName:"Foo", LastName:"Bar", Password:"123", Headline:"Tuna"}
This is the method which creates an User.
// localhost:12345/api/controller/create
[ActionName("create")]
public HttpResponseMessage PostUser(User user)
{
if (ModelState.IsValid)
{
db.Users.Add(user);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, user);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = user.UserId }));
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
However I'm able to make a successful POST request without the /create at the end of URL. I'm assuming that the reason for the success POST request is because whether or not the action name is present it will search for a Post in the method name.
So my question is, what's the purpose of the action name then? How can I make it so it's a must in the URL? Also why is it accepting both JSON requests and how can I make it accept one or the other.
EDIT:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Routing by Action-name
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{username}",
defaults: new { username = RouteParameter.Optional }
);
Thank you for your time
/twice
How can I make it so it's a must in the URL?
By changing your route definition in ~/App_Start/WebApiConfig.cs and making the action name explicitly appear in your route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Also why is it accepting both JSON requests and how can I make it accept one or the other.
Because the Web API uses JSON.NET as JSON serializer which accepts both of them.
If you look in App_Start for the route that's registered for your API calls you'll see:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You'll notice that no action is specified. If you want to map to action names, you can change it to:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
But then you'll always be required to set an action (unless it's a GET which is the default). You can no longer POST directly toe controller, you'll always need to add an action.
Per this link the default behavior is to look for a method that starts with the HTTP method used:
To find the action, Web API looks at the HTTP method, and then looks
for an action whose name begins with that HTTP method name. For
example, with a GET request, Web API looks for an action that starts
with "Get...", such as "GetContact" or "GetAllContacts". This
convention applies only to GET, POST, PUT, and DELETE methods. You can
enable other HTTP methods by using attributes on your controller.
We’ll see an example of that later.
Another option is to create different controllers for your different GET calls, which is what I've done in the past.

How do I get ASP.NET Web API working with versioning and the Help Page extension?

I've implemented a versioning framework into my WebAPI application, and would very much like to get it working with the new Help Page extension from Microsoft.
Microsoft.AspNet.WebApi.HelpPage
SDammann.WebApi.Versioning
Quite simply, I don't know how to get them both working together. I have 2 projects:
AdventureWorks.Api (The main host/root application)
AdventureWorks.Api.v1 (A class library containing the first version of the API)
The versioning works as expected.
I've tried installing the HelpPage package on the root application, and when I browse to the help page, it appears none of the controllers are being found. Internally I believe it uses:
Configuration.Services.GetApiExplorer().ApiDescriptions
This returns no results, so I get an error.
Can anyone assist me in getting both of these packages working together?
Edit:
In the beginning, I wasn't sure this was a routing problem, but recent comments seem to suggest otherwise. Here is my RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "SldExportAliasApi",
routeTemplate: "api/v{version}/sld-export/{id}",
defaults: new { id = RouteParameter.Optional, controller = "Export" }
);
routes.MapHttpRoute(
name: "LinkRoute",
routeTemplate: "api/v{version}/link/{system}/{deployment}/{view}",
defaults: new { controller = "Link" }
);
routes.MapHttpRoute(
name: "DefaultSubParameterApi",
routeTemplate: "api/v{version}/{controller}/{id}/{param}",
defaults: new { id = RouteParameter.Optional, param = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v{version}/{controller}/{action}/{id}",
defaults: new { action = "Index", id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
You need to get a documentation XML file from your project AdventureWorks.Api.v1 project and place it in the bin folder of the AdventureWorks.Api project:
Then add these lines to your Application_Start method:
// enable API versioning
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new RouteVersionControllerSelector(GlobalConfiguration.Configuration));
GlobalConfiguration.Configuration.Services.Replace(typeof(IApiExplorer), new VersionedApiExplorer(GlobalConfiguration.Configuration));
GlobalConfiguration.Configuration.Services.Replace(typeof(IDocumentationProvider),
new XmlCommentDocumentationProvider(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) +
"\\Adventure.Api.v1.XML"));
Then you can view your API with the documentation.
Sometimes the version number does not get to be picked up correctly, and replaced by ???
To fix this add:
if (api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace != null)
{
var versionName = api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace.Replace(".Controllers", "").Split('.').Last();
api.RelativePath = api.RelativePath.Replace("v???", versionName);
}
to your ApiGroup.cshtml exactly at this place:
#foreach (var api in Model)
{
if (api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace != null)
{
var versionName = api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace.Replace(".Controllers", "").Split('.').Last();
api.RelativePath = api.RelativePath.Replace("v???", versionName);
}
<tr>
<td class="api-name">#api.HttpMethod.Method #api.RelativePath</td>
<td class="api-documentation">
#if (api.Documentation != null)
{
<p>#api.Documentation</p>
}
else
{
<p>No documentation available.</p>
}
</td>
</tr>
}
This should do the trick!
I couldn't figure out how to comment on a post :( I think this should probably be a comment under the marked answer for this question but SDamman is updated and all I needed to do was this
// enable API versioning
GlobalConfiguration.Configuration.Services.Replace(typeof(System.Web.Http.Dispatcher.IHttpControllerSelector),
new SDammann.WebApi.Versioning.RouteVersionedControllerSelector(GlobalConfiguration.Configuration));
GlobalConfiguration.Configuration.Services.Replace(typeof(IApiExplorer), new SDammann.WebApi.Versioning.VersionedApiExplorer(GlobalConfiguration.Configuration));
There is a type called VersionedApiExplorer and it works great. Hope this helps the solution is much easier now.
EDIT:
I realized after trying to get help working again myself that my answer wasn't obvious at all.
The ONLY thing you need to do to get help pages working is replace the global configs IApiExplorer, that's it. Just do it right after you change the handler per sdammans instructions.
I agree with #mortware, the default routing for web api would mean your url should look something like "site/api/controllerName/" if you're using default Get()/Post() methods. If you're using specificly named methods then the route looks something like "site/api/controllerName/methodName".
I've also run into difficulty with the parameter names. Eg, if in your route specified in /App_Start/WebApiConfig.cs you have;
// Controller with ID
// To handle routes like `/api/VTRouting/1`
config.Routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" } // Only integers
);
// Controllers with Actions
// To handle routes like `/api/VTRouting/route`
config.Routes.MapHttpRoute(
name: "ControllerAndAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" } // Only integers
);
then the method parameter for your http verb must have a parameter called "id", eg;
// url: site/api/controller/<int>
public HttpResponseMessage Get(int id) { return null; /*dummy*/ }
// url: site/api/controller/<int>
public HttpResponseMessage Post(int id) { return null; /*dummy*/ }
// url: site/api/controller/SomeAction/<int>
public HttpResponseMessage SomeAction(int id) { return null; /*dummy*/ }
If you have something like;
public HttpResponseMessage Get(int myID) { return null; /*dummy*/ }
It will not work as the "myID" parameter doesn't match that {id} specified in the route. As #OakNinja pointed out, we'll need your routing in the WebApiConfig.cs to help you pinpoint the exact cause

Categories