I'm trying to return route values in my view using a function:
VIEW:
#functions {
public Dictionary<string, object> GetFilters()
{
var filters = new Dictionary<string, object>();
var f_phrase = this.Request["f_phrase"] ?? string.Empty;
var f_site = this.Request["f_site"] ?? string.Empty;
var f_device = this.Request["f_device"] ?? string.Empty;
var f_startdate = this.Request["f_startdate"] ?? string.Empty;
var f_enddate = this.Request["f_enddate"] ?? string.Empty;
var f_package = this.Request["f_package"] ?? string.Empty;
filters.Add("f_phrase", f_phrase);
filters.Add("f_site", f_site);
filters.Add("f_device", f_device);
filters.Add("f_startdate", f_startdate);
filters.Add("f_enddate", f_enddate);
filters.Add("f_package", f_package);
return filters;
}
}
Then on my link I want to be able to do this:
#Html.ActionLink("Export file", "Export", "Log", new { #GetFilters() }, new { #class="iconDocumentText"})
but doesn't work.
From the MSDN on the routeValues parameter:
An object that contains the parameters for a route. The parameters are
retrieved through reflection by examining the properties of the
object. The object is typically created by using object initializer
syntax.
So you need to construct an object and return that instead of returning a dictionary (not sure, but maybe you can achieve this with dynamic/ExpandoObject or just create a custom type with properties named according to your route values).
Example:
public class FilterRouteValues
{
public string f_phrase { get; set; }
// ...
}
public FilterRouteValues GetFilters()
{
// Read from request, fill an instance of FilterRouteValues
}
Markup:
#Html.ActionLink("Export file", "Export", "Log", GetFilters(), ...)
Related
I imagine this must be pretty basic, but all my search results only show how to pass data the other way ( model to controller, controller to view, and then view back to controller, but nothing from controller back to model(class). I am trying to pass user input for a search parameter into the query string for an API. In the view:
<form method="post" action="~/Models/Search">
<input name="artist" placeholder="Enter artist's name" />
#{Search temp = new Search();
if (Request.Form["artist"] != null) //this method doesn't work; trying to get user response and pass it back to search class;
{ temp.Artist = Request.Form["artist"].ToLower(); } //have to hardcode search parameter at this time
}
<!--<button>Click Me</button>-->
<script>
var weather = Object();
$(document).ready(function () {
$('button').click(function () {
// var artist = $("#artist").val();
$.get("#Url.Action("SearchArtist", "Home")", function (response) {
console.log(response);
artistInfo = ko.mapping.fromJS(response); //populate the artist search object
ko.applyBindings(artistInfo);
});
});
});
The class:
public class Search
{
string artist;
public string Artist { get; set;}
public Object getArtistInfo()
{
string appID = "*************** ";
artist = "metallica";
string url = "http://developer.echonest.com/api/v4/artist/search?api_key="+ appID + "&format=json&name=" + artist + "&results=1&bucket=genre&bucket=songs";
//synchronous client;
var client = new WebClient();
var content = client.DownloadString(url);
var serializer = new JavaScriptSerializer();
var jsonContent = serializer.Deserialize<Object>(content);
return jsonContent;
}
}
The controller:
public ActionResult ArtistInfo()
{
return View();
}
public JsonResult SearchArtist()
{
Search artist = new Search();
return Json(artist.getArtistInfo(), JsonRequestBehavior.AllowGet);
}
Try to add attributes (runat="server" and id="chooseyourid") to html forms results below :
if you want to access to values in your code behinde you just need to write the id of your element like below :
/artiste.value;/
I'm trying to create a select list. I've created it just fine using a collection from my viewmodel that allows me to set each option's value and text with the following code:
#Html.DropDownListFor(model => model.Networks, new SelectList(Model.Networks, "NetworkID", "Name"), new { #class="form-control" })
Model.Networks contains another property called CountryId. I'd like to add an attribute to each option tag so it looks like:
<option value="[NetworkId]" data-countryId="[CountryId]">Canada</option>
Which way should I go about doing this?
You can create a Form Helper class to create a custom drop down list, and create a custom 'selectListItem' class that has an extra property 'itemsHtmlAttributes' of type IDictionary - see below. You may need to play around with the 'id' or 'name' attributes to get the default model binding working. Below is a bit messy, I would suggest using TagBuilder to build the 'select' and 'option' tags:
public class SelectListItemCustom : SelectListItem
{
public IDictionary<string, object> itemsHtmlAttributes { get; set; }
}
public static class FormHelper
{
public static MvcHtmlString DropDownListForCustom(this HtmlHelper htmlHelper, string id, List<SelectListItemCustom> selectListItems)
{
var selectListHtml = "";
foreach (var item in selectListItems)
{
var attributes = new List<string>();
foreach (KeyValuePair<string, string> dictItem in item.itemsHtmlAttributes)
{
attributes.Add(string.Format("{0}='{1}'", dictItem.Key, dictItem.Value));
}
// do this or some better way of tag building
selectListHtml += string.Format(
"<option value='{0}' {1} {2}>{3}</option>", item.Value,item.Selected ? "selected" : string.Empty,string.Join(" ", attributes.ToArray()),item.Text);
}
// do this or some better way of tag building
var html = string.Format("<select id='{0}' name='{0}'>{1}</select>", id, selectListHtml);
return new MvcHtmlString(html);
}
}
VIEW:
#{
var item = new SelectListItemCustom { Selected = true, Value = "123", Text = "Australia", itemsHtmlAttributes = new Dictionary<string, object> { { "countrycode", "au" } } };
var items = new List<SelectListItemCustom> { item };
Html.Raw(Html.DropDownListForCustom("insertIdHere", items))
}
I am using mvc3. is it possible to give controller and action a display name.
[DisplayName("Facebook Employee")]
public class EmployeeController : Controller
in my breadcrumb, I will get the controller name and action name
#{
var controllerName = ViewContext.RouteData.Values["Controller"];
var actionName = ViewContext.RouteData.Values["Action"];
}
I expect to see "Facebook Employee", but its not working.
You'll have to reflect on the Controller type itself, using GetCustomAttributes. Use ViewContext.Controller to get a reference to the controller itself. Something like this:
string controllerName;
Type type = ViewContext.Controller.GetType();
var atts = type.GetCustomAttributes(typeof(DisplayNameAttribute), false);
if (atts.Length > 0)
controllerName = ((DisplayNameAttribute)atts[0]).DisplayName;
else
controllerName = type.Name; // fallback to the type name of the controller
Edit
To do similar for an action, you need to first reflect on the method, using Type.GetMethodInfo:
string actionName = ViewContext.RouteData.Values["Action"]
MethodInfo method = type.GetMethod(actionName);
var atts = method.GetCustomAttributes(typeof(DisplayNameAttribute), false);
// etc, same as above
public static class HLP
{
public static string DisplayNameController(this WebViewPage wvp)
{
if (wvp.ViewBag.Title != null && (wvp.ViewBag.Title as string).Trim().Length > 0)
return wvp.ViewBag.Title;
ControllerBase Controller = wvp.ViewContext.Controller;
try
{
DisplayNameAttribute[] attr = (DisplayNameAttribute[])Controller.GetType().GetCustomAttributes(typeof(DisplayNameAttribute), false);
string DisplayName = attr[0].DisplayName;
return DisplayName;
}
catch (Exception)
{
return Controller.ToString();
}
}
public static string DisplayNameAction(this WebViewPage wvp)
{
string actionName = wvp.ViewContext.RouteData.Values["Action"].ToString();
try
{
Type type = wvp.ViewContext.Controller.GetType();
MethodInfo method = type.GetMethod(actionName);
DisplayNameAttribute[] attr = (DisplayNameAttribute[])method.GetCustomAttributes(typeof(DisplayNameAttribute), false);
string DisplayName = attr[0].DisplayName;
return DisplayName;
}
catch (Exception)
{
return actionName;
}
}
}
<title>#this.DisplayNameAction()</title>
I am currently trying to write a custom authentication filter and I need to access the dto that is being passed as a parameter to the action in my filter. Lets say I have an action like this
[AuthenticateProfile]
public ActionResult EditProfile(ProfileDTO profileDto)
{
if (ModelState.IsValid)
{
// Do crazy stuff
}
return something....
}
I need to do my authentication based on some of the properties that are inside profiledto object.
I want to know how I can get this object inside my filter from AuthorizationContext.
Here's how I did it:
var parameters = filterContext.ActionDescriptor.GetParameters();
var values = parameters.Select(s => new
{
Name = s.ParameterName,
Value = filterContext.HttpContext.Request[s.ParameterName]
});
Assuming that your logic is happening in OnActionExecuting (meaning before the actual controller action is run), then one way of doing this (as outlined here) would be:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.Controller.ViewData.ModelState.IsValid)
return;
var profileDto = filterContext.ActionParameters.SingleOrDefault(ap => ap.Key == "profileDto").Value;
if (profileDto != null)
{
// do something with profileDto
}
}
ASP.NET Core
var parameters =
filterContext
.ActionDescriptor
.Parameters
.Select(s => new
{
Name = s.Name,
Value = context.HttpContext.Request.Query[s.Name]
});
extension
public static StringValues? Parameter(this AuthorizationFilterContext context, string name)
{
var parameter = context.ActionDescriptor.Parameters.FirstOrDefault(it => it.Name == name);
if (parameter == null) return null;
return context.HttpContext.Request.Query[parameter.Name];
}
use
var parameter = context.Parameter("id");
Here are some examples for you to try out:
public class AuthenticateProfileAttribute : AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext) {
// Looping through named parameters
foreach (string name in filterContext.HttpContext.Request.QueryString.AllKeys) {
var value = filterContext.HttpContext.Request.QueryString[name];
// do something with the iteration
Console.WriteLine(name + ": " + value);
}
// Looping through un-named parameters a.k.a route parameters
foreach (KeyValuePair<string, object> parameter in filterContext.RouteData.Values) {
var name = parameter.Key;
var value = parameter.Value;
// do something with the iteration
Console.WriteLine(name + ": " + value);
}
// Get single named parameter
string parameter = filterContext.HttpContext.Request.QueryString["parameter"];
// Get single route parameter
parameter = filterContext.RouteData.Values["parameter"].ToString();
}
}
Using MVC 5.
I have a method that returns an array (string[]) and I'm trying to pass this array of strings into an Action Link so that it will create a query string similar to:
/Controller/Action?str=val1&str=val2&str=val3...etc
But when I pass new { str = GetStringArray() } I get the following url:
/Controller/Action?str=System.String%5B%5D
So basically it's taking my string[] and running .ToString() on it to get the value.
Any ideas? Thanks!
Try creating a RouteValueDictionary holding your values. You'll have to give each entry a different key.
<% var rv = new RouteValueDictionary();
var strings = GetStringArray();
for (int i = 0; i < strings.Length; ++i)
{
rv["str[" + i + "]"] = strings[i];
}
%>
<%= Html.ActionLink( "Link", "Action", "Controller", rv, null ) %>
will give you a link like
<a href='/Controller/Action?str=val0&str=val1&...'>Link</a>
EDIT: MVC2 changed the ValueProvider interface to make my original answer obsolete. You should use a model with an array of strings as a property.
public class Model
{
public string Str[] { get; set; }
}
Then the model binder will populate your model with the values that you pass in the URL.
public ActionResult Action( Model model )
{
var str0 = model.Str[0];
}
This really annoyed me so with inspiration from Scott Hanselman I wrote the following (fluent) extension method:
public static RedirectToRouteResult WithRouteValue(
this RedirectToRouteResult result,
string key,
object value)
{
if (value == null)
throw new ArgumentException("value cannot be null");
result.RouteValues.Add(key, value);
return result;
}
public static RedirectToRouteResult WithRouteValue<T>(
this RedirectToRouteResult result,
string key,
IEnumerable<T> values)
{
if (result.RouteValues.Keys.Any(k => k.StartsWith(key + "[")))
throw new ArgumentException("Key already exists in collection");
if (values == null)
throw new ArgumentNullException("values cannot be null");
var valuesList = values.ToList();
for (int i = 0; i < valuesList.Count; i++)
{
result.RouteValues.Add(String.Format("{0}[{1}]", key, i), valuesList[i]);
}
return result;
}
Call like so:
return this.RedirectToAction("Index", "Home")
.WithRouteValue("id", 1)
.WithRouteValue("list", new[] { 1, 2, 3 });
Another solution that just came to my mind:
string url = "/Controller/Action?iVal=5&str=" + string.Join("&str=", strArray);
This is dirty and you should test it before using it, but it should work nevertheless. Hope this helps.
There is a library called Unbinder, which you can use to insert complex objects into routes/urls.
It works like this:
using Unbound;
Unbinder u = new Unbinder();
string url = Url.RouteUrl("routeName", new RouteValueDictionary(u.Unbind(YourComplexObject)));
This is a HelperExtension solving array and IEnumerable properties troubles :
public static class AjaxHelperExtensions
{
public static MvcHtmlString ActionLinkWithCollectionModel(this AjaxHelper ajaxHelper, string linkText, string actionName, object model, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
{
var rv = new RouteValueDictionary();
foreach (var property in model.GetType().GetProperties())
{
if (typeof(ICollection).IsAssignableFrom(property.PropertyType))
{
var s = ((IEnumerable<object>)property.GetValue(model));
if (s != null && s.Any())
{
var values = s.Select(p => p.ToString()).Where(p => !string.IsNullOrEmpty(p)).ToList();
for (var i = 0; i < values.Count(); i++)
rv.Add(string.Concat(property.Name, "[", i, "]"), values[i]);
}
}
else
{
var value = property.GetGetMethod().Invoke(model, null) == null ? "" : property.GetGetMethod().Invoke(model, null).ToString();
if (!string.IsNullOrEmpty(value))
rv.Add(property.Name, value);
}
}
return System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(ajaxHelper, linkText, actionName, rv, ajaxOptions, htmlAttributes);
}
}
I'd use POST for an array. Aside from being ugly and an abuse of GET, you risk running out of URL space (believe it or not).
Assuming a 2000 byte limit. The query string overhead (&str=) reduces you to ~300 bytes of actual data (assuming the rest of the url is 0 bytes).