Query string parameter vs regular parameter ASP.Net MVC 5 - asp.net

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

Related

Do I have to post objects seperately with Angular 7 and Asp.net?

I am trying to post objects to my server but the received objects have the value null.
Backend Code:
// Signature
public IActionResult Save(string num, string pat, string token, [FromBody]DataCl data, [FromBody]List<items> listItems)
// EDIT: Added class
public class Object
{
public List<items> listItems { get; set; }
public DataCl data { get; set; }
}
// So new signature
public IActionResult Save(string num, string pat, string token, [FromBody]Test test)
// The value is still null
Frontend Code:
post(num, data, list)
return this.http.post<any>(url,{data, list}, httpOptions).subscribe()
So the parameter num, pat and token are receiving the correct data but the data representing the body are not reciving any data - they are null.
With only one object it is working fine - the correct object was received but with two it does not work anymore but why? Is it something in the frontend code? Or backend?
Check the following article here
Don't apply [FromBody] to more than one parameter per action method.
The ASP.NET Core runtime delegates the responsibility of reading the
request stream to the input formatter. Once the request stream is
read, it's no longer available to be read again for binding other
[FromBody] parameters.
You cannot have two FromBody attributes. The from body is only read once.
{data, list} is one object anyway in javascript. There is no way to Post multiple objects in body, unless they are embedded.
{
object1: {}
object2: {}
}
And in you backend code:
class WrapperObjectResponse {
public Object1 = ...
public Object2 = ...
}
In your new signature, try this:
[Route("save/{num}/{pat}/{token}")]
public IActionResult Save(string num, string pat, string token, [FromBody]Test test)
And call like this:
return this.http.post<any>(url + '/' + num + '/' + pat + '/' + token + '/',{data: {}, list = []}, httpOptions).subscribe()

How to know which route is currently mapped

I have a route as
routes.MapRoute(
"User", // Route name
"Person/{action}", // URL with parameters
new { controller = "User" } // Parameter defaults
);
that means if I put url like
http://localhost/myApp/Person/Detail
then it should invoke Detail action of User controller, right?
Ok, I have done it and routing also works good, means it invoke action properly.
Now if I want to get controller name then I will use
ControllerContext.RouteData.Values["controller"];
and that will give me User, but I want it to be Person (i.e. as in URL). How can I get that?
The Request.Url property of Controller will return a Uri object containing details of the current url, including the segments.
string[] segments = Request.Url.Segments;
// returns ["/", "Person/", "Detail"]
string value = segments[1].Remove(segments[1].Length - 1);;
// returns "Person"
you can get controller name by following code
HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString();
If you're in a view, then you can do:
ViewContext.RouteData.Values["Controller"]
and for custom url you can define
[Route("myApp/Person/{action?}")]
public ActionResult View(string id)
{
if (!String.IsNullOrEmpty(id))
{
return View("ViewStudent", GetStudent(id));
}
return View("AllStudents", GetStudents());
}

Definition of routes with ASP.net and C #

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.

RedirectToAction usage in asp.net mvc

I want to post some questions about ASP.Net MVC. I am not familiar with web developing, But I was assigned to the web part of a project. We are doing the following: first, we create get & set properties for the person data:
public class Person
{
public int personID {get;set;}
public string personName {get;set;}
public string nric {get;set;}
}
and after login, we put the data in a class Person object and we use RedirectToAction like this:
return RedirectToAction("profile","person",new { personID = Person.personID});
It's working normally, but the parameter are shown in the URL. How can I hide them and also
can I hide the action name? Guide me the right way with some examples, please.
The parameter are shown in the URL because that is what the third parameter to RedirectToAction is - the route values.
The default route is {controller}/{action}/{id}
So this code:
return RedirectToAction("profile","person",new { personID = Person.personID});
Will produce the following URL/route:
/Person/Profile/123
If you want a cleaner route, like this (for example):
/people/123
Create a new route:
routes.MapRoute("PersonCleanRoute",
"people/{id}",
new {controller = "Person", action = "Profile"});
And your URL should be clean, like the above.
Alternatively, you may not like to use ID at all, you can use some other unique identifier - like a nickname.
So the URL could be like this:
people/rpm1984
To do that, just change your route:
routes.MapRoute("PersonCleanRoute",
"people/{nickname}",
new {controller = "Person", action = "Profile"});
And your action method:
public ActionResult Profile(string nickname)
{
}
And your RedirectToAction code:
return RedirectToAction("profile","person",new { nickname = Person.nickname});
Is that what your after?
If you don't want the parameter to be shown in the address bar you will need to persist it somewhere on the server between the redirects. A good place to achieve this is TempData. Here's an example:
public ActionResult Index()
{
TempData["nickname"] = Person.nickname;
return RedirectToAction("profile", "person");
}
And now on the Profile action you are redirecting to fetch it from TempData:
public ActionResult Profile()
{
var nickname = TempData["nickname"] as string;
if (nickname == null)
{
// nickname was not found in TempData.
// this usually means that the user directly
// navigated to /person/profile without passing
// through the other action which would store
// the nickname in TempData
throw new HttpException(404);
}
return View();
}
Under the covers TempData uses Session for storage but it will be automatically evicted after the redirect, so the value could be used only once which is what you need: store, redirect, fetch.
this may be solution of problem when TempData gone after refresh the page :-
when first time you get TempData in action method set it in a ViewData & write check as below:
public ActionResult Index()
{
TempData["nickname"] = Person.nickname;
return RedirectToAction("profile", "person");
}
now on the Profile action :
public ActionResult Profile()
{
var nickname = TempData["nickname"] as string;
if(nickname !=null)
ViewData["nickname"]=nickname;
if (nickname == null && ViewData["nickname"]==null)
{
throw new HttpException(404);
}
else
{
if(nickname == null)
nickname=ViewData["nickname"];
}
return View();
}
Temp data is capable of handling single subsequent request. Hence, value gone after refresh the page. To mitigate this issue, we can use Session variable also in this case. Try below:
public ActionResult Index(Person _person)
{
Session["personNickName"] = _person.nickName;
return RedirectToAction("profile", "person");
}
And in "profile" actionmethod:
public ActionResult profile()
{
Person nickName=(Person)Session["personNickName"];
if(nickName !=null)
{
//Do the logic with the nickName
}
}

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