Passing objects as url parameters c# - c#

I want to pass a Filters object as well as other things as query parameters into a url, for example something like:
{
"clientId": 2,
"date": "2017-01-01",
"filters": {
"days": { "monday", "tuesday", "wednesday" },
"months": { "january", "february" }
}
But I don't know how an object like filters in this example could get passed in by a query string parameter. I know you would normally have something that looks like:
https://localhost/path?clientId=2&date=2017-01-01&filters= ?????
Thanks!

Maybe is better to POST your data if they are complex instead sending as query string parameter. But anyway, if you want to send as query string you can do following:
Convert object into string
Encode string
Append as parameter
For following object (converted into string and removed spaces):
{
"clientId": 2,
"date": "2017-01-01",
"filters": {
"days": { "monday", "tuesday", "wednesday" },
"months": { "january", "february" }
}
I created encoded text which is safe to sending over network:
%7B%0A%22clientId%22%3A%202%2C%0A%22date%22%3A%20%222017-01-01%22%2C%0A%22filters%22%3A%20%7B%0A%22days%22%3A%20%7B%20%22monday%22%2C%20%22tuesday%22%2C%20%22wednesday%22%20%7D%2C%0A%22months%22%3A%20%7B%20%22january%22%2C%20%22february%22%20%7D%0A%7D
In your case it will be:
https://localhost/path?clientId=2&date=2017-01-01&filters=%7B%0A%22clientId%22%3A%202%2C%0A%22date%22%3A%20%222017-01-01%22%2C%0A%22filters%22%3A%20%7B%0A%22days%22%3A%20%7B%20%22monday%22%2C%20%22tuesday%22%2C%20%22wednesday%22%20%7D%2C%0A%22months%22%3A%20%7B%20%22january%22%2C%20%22february%22%20%7D%0A%7D
You can use meyerweb.com to test encoding/decoding but in C# you can research HttpUtility.UrlEncode() method which can be used in your situation too.

If you really want to pass parameters in a query string, this is an example using ASP.NET MVC.
Create a route:
routes.MapRoute(
name: "Custom",
url: "{controller}/{action}/{clientId}/{date}/{filtersDay}/{filtersMonth}",
defaults: new { controller = "Home", action = "CustomQueryString" }
);
You can repeat an item and the Model Binding will create a string array:
http://localhost/Home/CustomQueryString?clientId=1&date=2017-01-01&filtersDay=Friday&filtersDay=Monday&filtersMonth=April&filtersMonth=June
and then, you will have this:

I have a ToDictionary extension method, that converts objects into the query string and you can pass it via RouteValues, I pass Model.SearchCriteria, which is a complex object in the folllowing example:
<a href='#Url.Action("ExportAll",
new RouteValueDictionary(Model.SearchCriteria))'>Export All</a>
ToDictionary is an extension method:
public static class ToDictionaryExtensionMethod
{
public static IDictionary<string, object> ToDictionary(this object source)
{
return source.ToDictionary<object>();
}
}
Unfortunately the following code doesn't work:
#Html.ActionLink("Export All", "ExportAll",
new RouteValueDictionary(Model.SearchCriteria.ToDictionary()),
new { #class = "btn btn-default" })
This is because this version of ActionLink accepts routevalues as an object, not as a RouteValueDictionary (in MVC 5). So to make it work, I have to convert the Html attributes to a dictionary as well which uses the correct overload of Html.ActionLink:
#Html.ActionLink("Export All", "ExportAll",
new RouteValueDictionary(Model.SearchCriteria.ToDictionary()),
new Dictionary<string,object>{{"class","btn btn-default"}})

Related

Proper way of passing additional view data to EditorFor

If I pass view data like this
#(Html.EditorFor(x => x.User2, "GenericList",
new ViewDataDictionary {
{ "ListType", ListType.CustomValidation },
{ "Param1", ObjectType.Asset },
{ "Param2", "User2" },
{ "DefaultValue", Model.User2 }
}
))
I am unable to access the Values via ViewData["ListType"]. The keys are stored in a property called Keys, and the values are stored in a ICollection property called Values
Passing the ViewData this way
#Html.EditorFor(x => x.User2, "GenericList",
new { ListType = ListType.CustomValidation,
Param1 = ObjectType.Asset,
Param2 = "User2",
DefaultValue = Model.User2
}
)
means I can get the values easily like ViewData["ListType"].
I don't liek the 2nd method of doing things because it's using anonymous types instead of a ViewDataDictionary. How can I use the first method but still call the ViewData as I expect to be,

Unable to pass a native value in ActionLink under Razor while POCO instances work great

First I thought that I'm using the wrong overload again (a very common gotcha in the API - everybody trips over that one). But wouldn't you know? That's not it. I actually had the HTML attributes parameter too and I verified with intellisense that it's the route values I'm entering.
#Html.ActionLink("Poof", "Action", "Home", 10, new { #class = "nav-link" })
Nevertheless, it seem that the receiving method below only sees null and crashes as it can't make an integer out of it.
public ActionResult Record(int count) { ... }
I've tried a few things: changed parameter type to int? and string (the program stops crashing but the value is still null). I've tested to package the passed value as an object (with/without #).
#Html.ActionLink("Poof", "Record", "Home",
new { count = "bamse" },
new { #class = "nav-link" })
I can see that the anchor produced has my value as a query string, so the changes are there. However, I still get null only in the method.
What am I missing?
The weird thing is that the following works fine.
#Html.ActionLink("Poof", "Record", "Home",
new Thing(),
new { #class = "nav-link" })
public ActionResult Record(Thing count) { ... }
Your using the overload of #Html.ActionLink() that expects the 4th parameter to be typeof object. Internally the method builds a RouteValueDictionary by using the .ToString() value of each property in the object.
In your case your 'object' (an int) has no properties, so no route values are generated and the url will be just /Home/Action(and you program crashes because your method expects a non null parameter).
If for example you changed it to
#Html.ActionLink("Poof", "Action", "Home", "10", new { #class = "nav-link" })
i.e. quoting the 4th parameter, the url would now be /Home/Action?length=2 because typeof string has a property length and there a 2 characters in the value.
In order to pass a native value you need to use the format
#Html.ActionLink("Poof", "Action", "Home", new { count = 10 }, new { #class = "nav-link" })
Which will generate /Home/Action?count=10 (or /Home/Action/10 if you create a specific route definition with Home/Action/{count})
Note also that passing a POCO in your case only works correctly because your POCO contains only value type properties. If for example, it also contained a property which was (say) public List<int> Numbers { get; set; } then the url created would include ?Numbers=System.Collections.Generic.List[int] (and binding would fail) so be careful passing complex objects in an action link
Hard to say what might be wrong with your code from the information provided in your question but assuming total defaults (a newly created ASP.NET MVC application in Visual Studio), if you add the following markup in your ~/Views/Home/Index.cshtml:
Html.ActionLink(
"Poof",
"Record",
"Home",
new { count = "bamse" },
new { #class = "nav-link" }
)
and the following action in your HomeController:
public ActionResult Record(string count)
{
return Content(count);
}
upon clicking on the generated anchor, the correct action will be invoked and the correct parameter passed to it.
The generated markup will look like this:
<a class="nav-link" href="/Home/Record?count=bamse">Poof</a>
So I guess that now the question that you should be asking yourself is: how does my setup differs with what Darin has outlined here? Answering this question might hold the key to your problem.
UPDATE:
OK, now you seem to have changed your question. You seem to be trying to pass complex objects to your controller action:
public ActionResult Record(Thing count) { ... }
Of course this doesn't work as you might expect. So make sure that you pass every single property that you want to be available when constructing your anchor:
Html.ActionLink(
"Poof",
"Record",
"Home",
new { ThingProp1 = "prop1", ThingProp2 = "prop2" },
new { #class = "nav-link" }
)
Alternatively, and of course a much better approach to handle this situation is to attribute an unique identifier to your models, so that all its needed in order to retrieve this model from your backend is this identifier:
Html.ActionLink(
"Poof",
"Record",
"Home",
new { id = "123" },
new { #class = "nav-link" }
)
and then in your controller action simply use this identifier to retrieve your Thing:
public ActionResult Record(int id)
{
Thing model = ... fetch the Thing using its identifier
}

How do you initialize a FormCollection with properties in .NET?

I've tried the usual way of:
var form = new FormCollection { "WeekList" = weekfilter, "PracticeList" = practicefilter}
and all possible deviations I could think of, but ultimately had to seperate it apart as:
var form = new FormCollection();
form["WeekList"] = weekfilter;
form["PracticeList"] = practicefilter;
How can I initialize this inline? Is it possible? (I'm basically trying to mimic a form being submitted)
If you're using ASP.NET MVC (not core), you can initialize System.Web.Mvc.FormCollection like this:
var form = new FormCollection {
{"WeekList", weekfilter},
{"PracticeList", practicefitler}
}
Demo in .NET Fiddle
But I'm not the right computer to test this. I'm basing this on the .Add method of FormCollection begin declared as:
public virtual void Add(
string name,
string value
)
What types are the filter variables declared as?
If you're using Microsoft.AspNetCore.Http.FormCollection in aspnet core, you can initialize like this:
var formCol = new FormCollection(new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>
{
{ "Field1", "String Value 1" },
{ "Field2", "String Value 2" },
{ "Field3", "String Value 3" }
});
Demo in .NET Fiddle
Then, if you need to create a controller with test data, you can pass to the controller create method like this:
// Call create controller with test data
_controller.Create(formCol);

Is it possible to have route multiple parameters in asp.net mvc using maproute

I want a user to be able to access objects (could be JSON or XML) using a restful syntax rather than having to use query strings.
So instead of http://mywebsite.com/objects/get=obj1&get=obj2&get=someotherobject/ they could do something like http://mywebsite.com/objects/obj1/obj2/ and the xml/JSON would be returned. They could list the objects in any order just like you can with query strings.
In asp.net mvc you map a route like so:
routes.MapRoute(
"MyRoute",
"MyController/MyAction/{param}",
new { controller = "MyController", action = "MyAction", param = "" }
);
I would want to do something like:
routes.MapRoute(
"MyRoute",
"MyController/MyAction/{params}",
new { controller = "MyController", action = "MyAction", params = [] }
);
where the params array would contain each get.
Not quite.
You can create a wildcard parameter by mapping {*params}.
This will give you a single string containing all of the parameters, which you can then .Split('/').
You could use a catchall parameter
routes.MapRoute(
"MyRoute",
"MyController/MyAction/{*params}",
new { controller = "MyController", action = "MyAction"}
);
This would pass params as a string that you could split on a / to get an array.

ASP.NET MVC URL Routes

In ASP.NET MVC, is it possible to define routes that can determine which controller to use based on the data type of part of the URL?
For example:
routes.MapRoute("Integer", "{myInteger}", new { controller = "Integer", action = "ProcessInteger", myInteger = "" });
routes.MapRoute("String", "{myString}", new { controller = "String", action = "ProcessString", myString = "" });
Essentially, I want the following URLs to be handled by different controllers even though they have the same number of parts:
mydomain/123
mydomain/ABC
P.S. The above code doesn't work but it's indicative of what I want to acheive.
Yes, if you use constraints:
like so:
routes.MapRoute(
"Integers",
"{myInteger}",
new { controller = "Integer", action = "ProcessInteger"},
new { myInteger = #"\d+" }
);
If you put that route above your string route (that doesn't contain the constraint for #"\d+"), then it'll filter out any routes containing integers, and anything that doesn't have integers will be passed through and your string route will pick it up.
The real trick is that Routes can filter what's coming through based on Regular Expressions, and that's how you can determine what should pick it up.

Categories