Definition of routes with ASP.net and C # - asp.net

I have a problem with the definition of routes with ASP.net and C #, using the GET verb.
I get this URL,
http://123.45.67.89:39051/dev/point/save?name=125.25
I get this URL, where point changes according to where I want to search the data. The dev and save parameters are constant. The number of points is large, would need to make this could take any value with characters and numbers.
Modify the file WebApiConfig, trying it can take any value and did not work.
For example:
point12
point23
point24
MzaB342
Pozo123
MzaE258
WebApiConfig
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "dev/{points}/save",
defaults: new { points= RouteParameter.Optional }
constraints: new { points= #"^[a-z]+$" }
);
How can I do this?
It is correct that I have to put "^ [a-zA-Z0-9] + $" to take qur numbers, but if problem persists tel sent a query a requirement
http://123.45.67.89:39051/dev/values/save?name=125.25
Works fine.
If I want to consult:
http://123.45.67.89:39051/dev/point12/save?name=125.25
I get a 404 error, because there is no route point12, these are name within a database, and there are a large amount, could not generate a route for each of them, have to redirect them to one and then to decode this route and assign the value.
Or what I get after http://123.45.67.89:39051/dev/xxyyzz121/save take it correctly, I separate the information from the URL, which allows me to control the request for each point, using data binding or Parse Query.
I can not change the way I get the URL, that comes from another system.
Try to test the solution to put the path and when I enter the class, have an error in ActionResult, you need to load System.Web.Mvc, as I am using WebAPI without MVC, can not find it, if it gives me errors added in WebAPI other classes.
As could be done to define default routes.
Web.Api.Config Code:
namespace WebApp_dev
{
public static class WebApiConfig
{
public static void Register (HttpConfiguration config)
{
// Configuration API and Web services
// Web API routes
config.MapHttpAttributeRoutes ();
config.Routes.MapHttpRoute (
name: "DefaultApi"
routeTemplate "dev / {controller} / {id}",
defaults: new {controller = "values", id = RouteParameter.Optional,},
constraints: new {controller = # "^ [a-zA-Z0-9] + $"}
);
}
}
}
ValuesController.cs Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApp_dev.Controllers
{
public class ValuesController: ApiController
{
// GET api / values
public IEnumerable <string> Get ()
{
return new string [] {"value1", "value2"};
}
// GET api / values ​​/ 5
public string Get (int id)
{
return "value";
}
// POST api / values
public void Post ([FromBody] string value)
{
}
// PUT api / values ​​/ 5
public void Put (int id, [FromBody] string value)
{
}
// DELETE api / values ​​/ 5
public void Delete (int id)
{
}
}
}
No, the name of the route changes, they are values​​, the URL that can reach between muchs are:
/dev/nombre1/save?name1=123.56
or
/dev/point123/save?name12=12
or
/dev/pozo12/save?value1=13
or
/dev/mbz134/save?costo2=13
or
/dev/patag235/save?name8=13
What changes is name1, point123, pozo12, mbz134 etc. The amount is large, are stored in a database, and according to that comes, it is the search is done and the response sent.
The beginning of the URL /dev and end /save? It stays constant , what is after the save? (name1, name12, value1, costo2, name8) are the parameters that change and references also: name1, point123, pozo12, mbz134 etc, which also change.
Function properly, very good response.
One more question I need to separate a variable point name for which I access the data, for example:
http://123.45.67.89:8090/dev/nombre1/save?name1=12.56&di2=1&an1=5
stored in a variable (nombre_pto =), the value entered: name1
http://123.45.67.89:8090/dev/patag235/save?name1=3.56&name2=2.36&val4=5
stored in a variable (nombre_pto =), the value entered: patag235
http://123.45.67.89:8090/dev/mza341/save?name1=123.56&pos2=23.36
stored in a variable (nombre_pto =), the value entered: mza341
This will need to search the database and filter the information according to the parameters.
From already thank you very much
I could solve.
string url_completa = Request.RequestUri.AbsolutePath;
// Busco el nombre del punto de medicion-----------
int _indexPto = url_completa.IndexOf("/dev/");
url_completa=url_completa.Remove(0, (_indexPto + 5))

The regex in your constraint could be "^[a-zA-Z0-9]+$"

UPDATED according to changes that were done in question description.
To handle such urls you should:
Register wildcard route in WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "dev/{point}/save",
defaults: new { controller = "Values", action = "Save" },
constraints: new { point = #"^[a-zA-Z0-9]+$" }
);
Write your controller to process this route:
public class ValuesController : ApiController
{
[HttpGet]
public string Save(string point)
{
// in point variable you will get part between /dev and /save
// for /dev/point/save?name=125.25 url it will be "point"
// get parameters that follow by ? mark in url
var queryParams = GetQueryParams(Request);
// loop through all of them
foreach (var pair in queryParams)
{
string paramName = pair.Key; // for /dev/point/save?name=125.25 will be "name"
string paramvalue = queryParams[pair.Key]; // for /dev/point/save?name=125.25 will be 125.25
}
return "some value";
}
private Dictionary<string, string> GetQueryParams(HttpRequestMessage request)
{
return request.GetQueryNameValuePairs()
.ToDictionary(kv => kv.Key, kv => kv.Value,
StringComparer.OrdinalIgnoreCase);
}
}
Other urls are also fit this controller and this action.
Hope that finally it is what you were asking for.

Related

Query string parameter vs regular parameter ASP.Net MVC 5

I have been working on desktop applications mostly and thought to learn web development using ASP.Net MVC5 and thus going through the book by Jon Galloway. So I was reading about how you can pass the parameters to action methods using query string like
/Store/Browse?genre=Disco
or directly embed them in the url like
/Store/Details/5
Now the controller code that I wrote (taken from book) is below :
namespace MvcMusicStore.Controllers
{
public class StoreController : Controller
{
// GET: Store
public string Index()
{
return "Hello from Store.Index()";
}
public string Browse(string genre)
{
string message = HttpUtility.HtmlEncode("Store.Browser, Genre = " + genre);
return message;
}
public string Details(int id)
{
string message = "Store.Details, ID = " + id;
return message;
}
}
}
The url opens fine and the actions return the message as expected. But just to try I tried to pass the genre value by embedding it in the url like
/Store/Browse/Rap
but that doesn't work like it did for the Details() action. I thought it may have to do something with the datatype of genre, so I tried changing the data type of id in Details() to string as below :
public string Details(string id)
{
string message = "Store.Details, ID = " + id;
return message;
}
}
and opened the url
/Store/Details/5
and the Details() action returns message with id value 5, but when i do the same for Browse() action
/Store/Browse/Rap
the action doesn't return the message with genre value "Rap". I tried to pass the genre value and removed the html encoding to see if that had anything to do with it, but it didn't.
I looked at the post here but that didn't help either. Any comments appreciated.
Your using the Default route which is defined as
url: "{controller}/{action}/{id}",
and expects a value for id. When you use /Store/Browse/Rap, then the value of the 3rd segment ("Rap") will be bound to a paramater named id, but your Browse() method does not contain one (its named genre).
Either change the name of the parameter so its
public string Browse(string id)
and the value of id will be "Rap",
Or create a specific route definition and place it before the Default route (and keep the Browse() method as is)
routes.MapRoute(
name: "Browse",
url: "Store/Browse/{genre}",
defaults: new { controller = "Store", action = "Browse", genre = UrlParameter.Optional }
);
... // default route here
Side note: You do not need to change the type of the parameter in the Details method if your always passing a value that is a valid int

Can this be done using ASP.NET URL Rewriting

There are about 1700 articles listed on my website created using ASP.NET 4.0 Web Forms. These articles have a url format as:
http://www.mymymyarticles.com/Article.aspx?ID=400
I have explored ASP.NET Friendly URLs as well IIS URL Rewrite. These extensions are great but once a rule is created, they treat all url's generically.
Is it possible that I manually generate my own url string for every url that exists on my website? For eg:
I want to permanent redirect http://www.mymymyarticles.com/Article.aspx?ID=400 to http://www.mymymyarticles.com/this-is-a-very-long-url whereas
http://www.mymymyarticles.com/Article.aspx?ID=500 can be redirected to http://www.mymymyarticles.com/article/short-url and
http://www.mymymyarticles.com/Article.aspx?ID=523 can be redirected to http://www.mymymyarticles.com/very-short-url
So you can see there is no uniformity in the url's that I want to manually generate. Basically I want full control over the url's. How can I go about this. Will it affect performance?
Any examples are appreciated.
Do you have a way of mapping an ID to the url of the new page? If that is the case, you could probably achieve this with ASP.NET Routing. What I would do is start with defining a route:
var route = routes.MapRoute(
"LegacyDocument",
"Articles.aspx{*pathInfo}",
null,
constraints: new { pathInfo = new LegacyDocumentRouteConstraint() }
);
route.RouteHandler = new RedirectRouteHandler();
This route merely captures any requests for /articles.aspx, but it has a constraint and a custom route handler.
The purpose of the constraint is to ensure we at least have the ID query string property and it is a number:
public class LegacyDocumentRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.UrlGeneration)
{
// Don't both doing anything while we generate links.
return false;
}
string id = httpContext.Request.QueryString["id"];
if (string.IsNullOrWhiteSpace(id))
{
// No query string argument was provided.
return false;
}
int documentId;
if (!int.TryParse(id, out documentId))
{
// The Id is not a number.
return false;
}
// Set the output document Id in the route values.
values["documentId"] = documentId;
return true;
}
}
If the Id was not provided, or was not a number, we can't match to an existing document, so the route will be skipped over. But when the constraint is satisfied, we store a variable in the route values values["documentId"] = documentId so we can then use it again (without having to parse it from the query string again) later in the route handler:
public class RedirectRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext context)
{
int documentId = (int)context.RouteData.Values["documentId"];
return new RedirectLegacyDocumentHttpHandler(documentId);
}
private class RedirectLegacyDocumentHttpHandler : IHttpHandler
{
private int _documentId;
public RedirectHttpHandler(int documentId)
{
_documentId = documentId;
}
public bool IsReusable { get { return false; } }
public void ProcessRequest(HttpContext context)
{
var response = context.Response;
string url = ResolveNewDocumentUrl();
if (url == null)
{
// Issue a 410 to say the document is no longer available?
response.StatusCode = 410;
}
else
{
// Issue a 301 to the new location.
response.RedirectPermanent(url);
}
}
public string ResolveNewDocumentUrl()
{
// Resolve to a new url using _documentId
}
}
}
Route handlers perform the logic of mapping from ASP.NET routing back into the IHttpHandler logic of the ASP.NET runtime. In normal MVC, this would map to the standard MvcHandler which invokes controllers, but in our case, we need only to issue a redirect.
In the route handler, we grab our document Id from the route values, and create a new HTTP handler which performs the actual redirect. You'll need to plumb in the bit which you resolve what the actual new url would be (ResolveNewDocumentUrl), but generally it will resolve the url, if the url comes back as null, we'll issue a HTTP 410 Gone response to say to clients (and more importantly crawlers) that the item is no longer there, or it will issue an HTTP 301 Permanent Redirect with the appropriate location header to the new url.
I had overcome this by creating an xml file on the server with the below schema
<URLMapper>
<Code>1</Code>
<OLDURL>%Oldurl.aspx%</OLDURL>
<NEWURL>default.aspx</NEWURL>
<PermanentRedirect>true</PermanentRedirect>
<Order>1</Order>
<Status>true</Status>
</URLMapper>
Loaded this in Application_Start Event to an application variable (in the form of a datatable).
And in the Begin Request --
void Application_BeginRequest(object sender, EventArgs e)
{
if (Application["URLMapper"] == null) return;
DataTable dtURLs = Application["URLMapper"] as DataTable;
if (dtURLs.Rows.Count == 0) return;
string OrigUrl = HttpContext.Current.Request.Url.ToString().ToLower().Replace("'", "`");
DataRow[] drFound = dtURLs.Select("Status = true and '" + OrigUrl.Trim() + "' like oldurl", "Order",DataViewRowState.CurrentRows);
if (drFound.Length == 0) return;
string OldURL = drFound[0]["OldURL"].ToString().Replace("%","");
Response.RedirectPermanent(OrigUrl.Replace(OldURL, drFound[0]["NewURL"].ToString().Trim()), true);
return;
}

Web Api routing limitations?

Say I have this 2 actions in my Api Controller :
[HttpGet]
public Product ProductById(int id)
{
...
return item;
}
[HttpGet]
public string ProductByString(string st)
{
return "String Result";
}
I also have these 2 routes :
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new
{
id = RouteParameter.Optional
});
config.Routes.MapHttpRoute("DefaultApi2", "api/{controller}/{st}");
Running http://mysite:9000/api/products/1 will work.
It will catch the first route because there's a match.
if I wanted the STRING action version I must do :
http://mysite:9000/api/products/?st=blabla this will be using ALSO the first route. ( query string is not negotiation to match a route).
If I swap the routes , only the DefaultApi2 route is hit. ( I do understand why it is happening).
So my conclusion is that I will never be able to do both:
http://mysite:9000/api/products/1
And
http://mysite:9000/api/products/Guitar
In other words - the second route will never be a match.
Question
Besides
mentioning the {action} name in the route (not recommended ,web Api conventions)
using route attribute ( I dont use webApi 2)
What is the right way of solving this issue ? I can see how it can be complicated ( I 'll have to remember : only the int ID has the x/y/id thing , for others I must use querystring etc etc.... I can get lost easily - in a big application)
I guess it can be fixed with three routes:
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
contraint: new { id = #"\d+" }
);
// only matches numeric id's
config.Routes.MapHttpRoute("DefaultApi2", "api/{controller}/{st}");
// only matches having a parameter, no matter what data type
config.Routes.MapHttpRoute("DefaultApi3",
"api/{controller}/{id}",
new
{
id = RouteParameter.Optional
});
// matches the empty id

ASP.NET Web API Controller Function Executes when entering not mapped action route

I have a controller function which accepts a strongly typed model as parameter.
When i enter ANY url mapping to the controller but not on a specific action on the post request ,
then the controller executes this function instead of returning a 404 code.
When i Change the function's parameter to a primitive type variable then the problem does not occur. (i have tried using other strongly typed models as parameters and again the problem occurs)
Here's the function.
public class PhoneApiController : ApiController
{
[HttpPost]
public HttpResponseMessage RegisterApp(RegisterAppInfo appInfo)
{
var resp = Request.CreateResponse(HttpStatusCode.OK, new
{
Success = true,
AppId = 1000,
IdAlias = "QAUBC9",
appInfo = appInfo
});
return resp;
}
}
So when i enter for example
localhost:51464/Api/PhoneApi/Sajsdkasjdklajsasd
the function executes normally.!
I am using the default Route config
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I don't know if this is a bug or i am doing something wrong.
The URI /Api/PhoneApi/Sajsdkasjdklajsasd does match your route template api/{controller}/{id} with {controller} matching PhoneApi and {id} matching Sajsdkasjdklajsasd. I assume you are making a POST to this URI. So, Web API is mapping your request to the action method RegisterApp with [HttpPost] in the controller class PhoneApiController.
As far as the junk stuff in the URI, it gets mapped to {id}. But your parameter is RegisterAppInfo, which is a complex type and that gets bound from request body and not the URI. That's why it works when you have the complex type. The simple types are bound from URI, query string.
If you have the action method as public HttpResponseMessage RegisterApp(string id, Abc appInfo), you will see that this id parameter gets populated with "Sajsdkasjdklajsasd".
For MVC 4.5 this is the only thing that works
There is currently a bug about this.
Below is a work around in order to get the following route types work
api/{controller}/ //Get All
api/{controller}/{Id} //Get for id
api/{controller}/{Id}/{Action}/ //Get all for action for controller with Id
you need to do the following.
Change your routing over to. (Note the default action..)
config.Routes.MapHttpRoute(
name : "DefaultAPi",
routeTemplate : "api/{controller}/{id}/{action}",
defaults: new
{
id = RouteParameter.Optional,
action = "DefaultAction"
}
);
In your controller change the base methods over to
[ActionName("DefaultAction")]
public string Get()
{
}
[ActionName("DefaultAction")]
public string Get(int id)
{
}
[ActionName("SpaceTypes")]
public string GetSpaceTypes(int id)
{
}
Now everything should work as expected..
Thanks to Kip Streithorst full this, for a full explanation
The way routing works in Web API is:
First it matches the URI against route template. At this stage, it's not looking at your controller actions
Then it looks for a matching controller
Then it looks for a method where (a) the action matches (POST in this case) and (b) every simple parameter type is matched with a value from the URI.
If there is a complex parameter type, it tries to read that from the request body.
By default, Web API tries to bind "simple" parameter types (like int) from the URI, and tries to read complex types from the request body.
See here for details: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection

ASP.NET MVC SEO URL

My goal is to have the url routing as following:
http://www.abc.com/this-is-peter-page
http://www.abc.com/this-is-john-page
What is the simplest way to achieve this without placing controller name an function name in the url above? If page above not found, I should redirect to 404 page.
Addon 1: this-is-peter-page and this-is-john-page is not static content, but is from database.
Similar to KingNestor's implementation, you can also do the followings which will ease your work:
1) Write Your Model
public class MyUser{public String UserName{get; set;}}
2) add route to global asax
routes.MapRoute(
"NameRouting",
"{name}",
new { controller = "PersonalPage", action = "Index", username="name" });
3) Roll your own custom model binder derived from IModelBinder
public class CustomBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var username = getUserNameFromDashedString(request["username"]);
MyUser user = new MyUser(username);
return user;
}
}
4) in your action:
public ActionResult Index([ModelBinder(typeof(CustomBinder))] MyUser usr)
{
ViewData["Welcome"] = "Viewing " + usr.Username;
return View();
}
I personally wouldn't suggest a route like that but if it meets your needs you need to do something like:
Have the following route in your Global.asax file:
routes.MapRoute(
"NameRouting",
"{name}",
new { controller = "PersonalPage", action = "routeByName" });
Then, in your "PersonalPageController", have the following method:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult routeByName(string name)
{
switch (name)
{
case "this-is-peter-page": return View("PeterView");
case "this-is-john-page": return View("JohnView");
case Default: return View("NotFound");
}
}
Make sure you have the appropriate views: "PeterView", "JohnView" and "NotFound" in your Views/PersonalPage/.
I don't think this can be done. AFAIK ASP.NET MVC recognizes routing parameters via the character "/".
Your format, on the other hand, goes by "{controller}-is-{id}-{action}" -- so there is no way the controller can be distinguished from the id and the action.
I think using "/" characters doesn't affect or degrade SEO; it only affects human readability and retention of the URL.
Anyway, the following URL is possible: http://www.abc.com/this-is-the-page-of/Peter by adding another route in the Global.asax RegisterRoutes method:
routes.MapRoute(
"AnotherRoute",
"this-is-the-page-of/{id}",
new { controller = "PersonalPage", action = "Details", id = "" }
);
...assuming that PersonalPageController implements a Details ActionResult method that points to the desired page.

Resources