Angular to C# Http passing query parameters - c#

I think the C# HttpGet TEMPLATE is what I am missing, but here’s the details. On the Angular front end:
let params = new HttpParams()
.append('GDWarn', values[0].toString())
.append('GDLimit', values[1].toString())
.append('NDWarn', values[2].toString())
.append('NDLimit', values[3].toString())
let url = `${this.url}/CalibrationModelFile/UpdateLimits/${this.networkID}/${constID}/{params}`
Here I assume this.http.get(url, … will do some magic to recognize HttpParams is a set of QUERY parameters.
So the C# backend to receive the http request:
[HttpGet("CalibrationModelFile/UpdateLimits/{networkID:int}/{ConstituentID:int}/{values}")]
public async Task UpdateConstituentLimits(int networkID, int ConstituentID, [FromQuery] double[,] values)
I think the [FromQuery] may be right, but NOT enough. The {values] in the template probably should have something so we know it's QUERY PARMS?
Any thoughts?
Thanks in Advance, Yogi

Sending an Array as Query Parameters
If you're trying to get a set of query params and route params like this:
.../UpdateLimits/networkID/ConstituentID?values=array
you should send a request as shown in the sample:
.../UpdateLimits/1/2?values=3.0&values=4.0&values=5.0
Query Parameters are not Part of a Route
Action's arguments in C# will be:
[HttpGet("CalibrationModelFile/UpdateLimits/{networkID:int}/{ConstituentID:int}")]
public async Task UpdateConstituentLimits(int networkID, int ConstituentID, [FromQuery] double[] values)
In the above sample {values} is removed from the route because QueryParam is not a part of a route. Also, it's better to decorate route parameters with [FromRoute] attribute.
2D Array as Query Parameters
Now, if the case is a 2D array as a query param, a simple solution is converting a 2D array into a string and parse the string in the C# action as following code:
.../UpdateLimits/1/2?values=GDWarn:4.1,GDLimit:3.7,NDWarn:6.3,NDLimit:4.8
and parsing query string in the resulted action will be like this:
[HttpGet("{networkID:int}/{ConstituentID:int}")]
public IEnumerable<WeatherForecast> Get([FromRoute]int networkID,
[FromRoute]int ConstituentID, [FromQuery]string values)
{
// received string is: "GDWarn:4.1,GDLimit:3.7,NDWarn:6.3,NDLimit:4.8"
var dict = new Dictionary<string, double>();
foreach (var item in values.Split(','))
dict.Add(item.Split(':')[0], Convert.ToDouble(item.Split(':')[1]));
return (...)
}

Related

C#: How to get a value from a query string containing another url with multiple query params?

When a url containing a query params that has a URL containing query params like this:
https://example.com/login/login.ashx?redirect_url=https://example.com?test=11&test2=22&test3=33
How can I then get https://example.com?test=11&test2=22&test3=33from that Url?
I've tried this with no luck
redirectUrl = HttpUtility.ParseQueryString(HttpUtility.UrlEncode(context.Request.Url.Query)).Get("redirect_url");.
This results in:
https://example.com?test=11
and I want https://example.com?test=11&test2=22&test3=33
Any url parameter that contains url string separators should be UrlEncode'd first.
So you need to System.Net.WebUtility.UrlEncode(string value) your parameter url first.
var urlEncodedParameter = System.Net.WebUtility.UrlEncode("https://example.com?test=11&test2=22&test3=33");
then create url
var url = $"https://example.com/login/login.ashx?redirect_url={urlEncodedParameter}";
Then redirect to that.
And you will not need to parse that parameter on the controller.
It will work on the endpoint with signature like this
public IActionResult Login(string redirect_url)
First of all you need to get full url from browser:
string full_url = HttpContext.Request.GetDisplayUrl().ToString();
Then you need to check that full url for existing "redirect_url" in it and if it exists you can use Substring(int position) method. But before we need to know position of "redirect_url", so we can use LastIndexOf() method:
if (full_url.Contains("redirect_url"))
{
return full_url.Substring(full_url.LastIndexOf("redirect_url"));
}
If there is no "redirect_url" in your full_url we can simply return empty string like this:
return string.Empty;

Passing an array of integers from JavaScript to a controller action of List<int>

I am using javascript code to postdata to a controller action coded in C#. I am getting a null value when attempting to pass an array of integers in javascript created as follows:
var answers = [];
answers.push(1);
answers.push(2);
answers.push(3);
I can see from javascript alert that the values are going into array as when I try answers.tostring I am getting the value "1,2,3" as expected, I am then trying the following postdata
var postData = {
'surveyID': surveyID,
'questionID': questionID,
'answers': answers,
'answerText': null,
'comment': comment,
'questionType': questionType
};
This is targetting the following controller action but the array of integers does not seem to be carrying across to the controller action as my List is always null
[HttpPost]
public PartialViewResult GoForward(int surveyID, int questionID, List<int> answers, string answerText, string comment, string questionType)
{}
Any know how I can get the postdata array to translate to a List that is not null?
Because MVC expects the data in QueryString pattern like this:
answers=1&answers=2&answers=3
If you use jQuery to do AJAX, default is
answers[]=1&answers[]=2&answers[]=3
You could try traditional option
$.ajax({
//...
traditional:true,
//...
});
refer to https://api.jquery.com/jQuery.ajax/

RedirectToAction with unnamed querystring parameters

In MVC 2 I have a RedirectToAction call which I need to pass all of the querystring parameters. Unfortunately I can only find a way to pass named querystring parameters is there a way of passing all querystring parameters regardless.
We have one named parameter, id but we just want to append all of the rest onto the end of the URL without setting them explicitly.
return RedirectToAction("index", "enquiry", new { id = enquiryCacheId.ToString()});
You cannot pass COMPLEX objects in URLs, so that kills the option of passing on Complex types in new { }.
One option what you are left with is to encode the querystring and then send that in 'id'. Say for examples, you have querystring as follows name=rami&gender=male. Then you can encode it with HttpUtility.UrlEncode(), then set it to id=[encoded string]. On the other action (retrieving side) you can get id value and then use HttpUtility.UrlDecode() to decode the string. Then finally you can use HttpUtility.ParseQueryString() to split the querystring into NameValueCollection.
If above suggestion is not what you are looking for then. you need to add all querystring parameters to new { } in the RedirectToAction(). If you want to customize it then you might need to go to ASP.Net MVC Source code #CodePlex and make your own builds (which I think not worthy for this kind of requirement).
I have an extension method that I use to modify the querystring of the current URL:
public static string ModifyQueryString(this UrlHelper helper, NameValueCollection updates, IEnumerable<string> removes)
{
var request = helper.RequestContext.HttpContext.Request;
var url = request.Url.AbsolutePath;
var query = HttpUtility.ParseQueryString(request.QueryString.ToString());
updates = updates ?? new NameValueCollection();
foreach (string key in updates.Keys)
{
query.Set(key, updates[key]);
}
removes = removes ?? new List<string>();
foreach (string param in removes)
{
query.Remove(param);
}
if (query.HasKeys())
{
return string.Format("{0}?{1}", url, query.ToString());
}
else
{
return url;
}
}
But, if you need to modify an arbitrary URL, it should be easy enough to modify. You would just need to add a parameter to accept an arbitrary URL, and then instead of getting the URL/querystring from the HttpContext, you just split the passed URL at ?. The rest of the code should work the same.

Passing an array to Html.ActionLink()

I followed this example:
ASP.NET MVC - Pass array object as a route value within Html.ActionLink(...)
But, my Action is always called with null. What am I doing wrong?
foreach (OrderDetail od in order.OrderDetails)
{
rvd.Add("key" + count++, productID);
rvd.Add("key" + count++, productName);
}
#Html.ActionLink(linkText, "Renew", "Orders", rvd, new Dictionary<string, object>())
The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish..., but I get a null parameter in my Action below:
public ActionResult Renew(RouteValueDictionary rvd)
{
// 'rvd' is null here!
}
Please note: I don't know the number of parameters in advance.
The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish...
No, this is not a correct url. A correct url would have looked like this:
?%5B0%5D.Key=123&%5B0%5D.Value=dog&%5B1%5D.Key=456&%5B1%5D.Value=cat...
which would have mapped to:
public ActionResult Renew(Dictionary<int, string> rvd)
{
...
}
You could write a custom ActionLink to generate this url:
public static class LinkExtensions
{
public static IHtmlString MyActionLink(
this HtmlHelper html,
string linkText,
string actionName,
string controllerName,
IDictionary<string, string> parameters
)
{
var a = new TagBuilder("a");
var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
var query = string.Join("&", parameters.Select((x, i) => string.Format("[{0}].Key={1}&[{0}].Value={2}", i, urlHelper.Encode(x.Key), urlHelper.Encode(x.Value))));
var url = string.Format(
"{0}?{1}",
urlHelper.Action(actionName, controllerName, null, html.ViewContext.HttpContext.Request.Url.Scheme),
query
);
a.Attributes["href"] = url;
a.SetInnerText(linkText);
return new HtmlString(a.ToString());
}
}
which you could use like this in your view:
#Html.MyActionLink(
linkText,
"Renew",
"Orders",
order.OrderDetails.ToDictionary(x => x.ProductID.ToString(), x => x.ProductName)
)
You can read more about the correct wire format for binding to various collections in this blog post.
I imagine what is happening is you are expecting the model binder to bind your array to a RouteValueDictionary, but the model binder doesn't know that key0=dog&key1=cat&key2=fish is supposed to be a dictionary. I would recommend changing your code to accept a string array. To do this, your query string needs to look something like this: ?rvd=dog&rvd=cat&rvd=fish
And your Action...
public ActionResult Renew(string[] rvd)
{
// 'rvd' is no longer null here!
}
The important part is rvd is the parameter name in your action, as well as the name of each element in the querystring: ?rvd=dog&rvd=cat&rvd=fish. If you really want to use a dictionary instead of a string array, then your querystring should look like this: ?rvd[0]=dog&rvd[1]=cat&rvd[2]=fish, giving each item an array index, but you would probably have to change your parameter from RouteValueDictionary to Dictionary<string,string>, I'm not quite sure. More info here. EDIT: See Darin's comment about binding to a dictionary, as I believe his is correct.
You may have to write your own extension for Html.ActionLink that accepts an array (or whatever OrderDetails is) and creates the querystring as an array. This looks like a pretty good starting place.

How do I accept an array as an ASP.NET MVC controller action parameter?

I have an ASP.net MVC controller called Designs that has an action with the following signature:
public ActionResult Multiple(int[] ids)
However, when I try to navigate to this action using the url:
http://localhost:54119/Designs/Multiple?ids=24041,24117
The ids parameter is always null. Is there any way to get MVC to convert the ?ids= URL query parameter into an array for the action? I've seen talk of using an action filter but as far as I can tell that will only work for POSTs where the array is passed in the request data rather than in the URL itself.
The default model binder expects this url:
http://localhost:54119/Designs/Multiple?ids=24041&ids=24117
in order to successfully bind to:
public ActionResult Multiple(int[] ids)
{
...
}
And if you want this to work with comma separated values you could write a custom model binder:
public class IntArrayModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (value == null || string.IsNullOrEmpty(value.AttemptedValue))
{
return null;
}
return value
.AttemptedValue
.Split(',')
.Select(int.Parse)
.ToArray();
}
}
and then you could apply this model binder to a particular action argument:
public ActionResult Multiple([ModelBinder(typeof(IntArrayModelBinder))] int[] ids)
{
...
}
or apply it globally to all integer array parameters in your Application_Start in Global.asax:
ModelBinders.Binders.Add(typeof(int[]), new IntArrayModelBinder());
and now your controller action might look like this:
public ActionResult Multiple(int[] ids)
{
...
}
To extend on Darin Dimitrov's answer, something you can get away with is accepting a simple string in your URL parameter and converting it to an array yourself:
public ActionResult Multiple(string ids){
int[] idsArray = ids.Split(',').Select(int.Parse).ToArray();
/* ...process results... */
}
If you get a parse error while doing this (because someone passed you a malformed array), you can cause your exception handler to return a 400 Bad Request error instead of the default, more unfriendly 404 Not Found error that MVC returns when an endpoint is not found.
You can also use this URL format, and ASP.NET MVC will do everything for you. But, remember to apply URL encoding.
?param1[0]=3344&param1[1]=2222
I don't know where Groky's URL string was coming from, but I had the same problem with some javascript calling my controller/action. It would build up a URL of null, 1, or many "IDs" from a multiple-select list (which is unique to the solution I'm going to share).
I copy/pasted Darin's custom model binder and decorated my action/parameter, but it didn't work. I still got null valued int[] ids. Even in the "safe" case where I actually did have many IDs.
I ended up changing the javascript to produce an ASP.NET MVC friendly parameter array like
?ids=1&ids=2
I had to do some silly stuff, though
ids || [] #=> if null, get an empty array
[ids || []] #=> if a single item, wrap it in an array
[].concat.apply([], ...) #=> in case I wrapped an array, flatten it
So, the full block was
ids = [].concat.apply([], [ids || []])
id_parameter = 'ids=' + ids.join('&ids=')
It's messy, but it's the first time I had to hack like this in javascript.
.Net Core Answer
For those coming here in recent times, you can do this in .Net Core with:
http://localhost:54119/Designs/Multiple?ids=24041&ids=24117
and:
public ActionResult Multiple([FromQuery] int[] ids)
{
...
}

Categories

Resources