What is the replacement of Controller.ReadFromRequest in ASP.NET MVC? - c#

I am attempting to update a project from ASP.NET MVC Preview 3 to Preview 5 and it seems that Controller.ReadFromRequest(string key) has been removed from the Controller class. Does anyone know of any alternatives to retrieving information based on an identifier from a form?

Looks like they've added controller.UpdateModel to address this issue, signature is:
UpdateModel(object model, string[] keys)
I haven't upgraded my app personally, so I'm not sure of the actual usage. I'll be interested to find out about this myself, as I'm using controller.ReadFromRequest as well.

Not sure where it went. You could roll your own extension though:
public static class MyBindingExtensions
{
public static T ReadFromRequest < T > (this Controller controller, string key)
{
// Setup
HttpContextBase context = controller.ControllerContext.HttpContext;
object val = null;
T result = default(T);
// Gaurd
if (context == null)
return result; // no point checking request
// Bind value (check form then query string)
if (context.Request.Form[key] != null)
val = context.Request.Form[key];
if (val == null)
{
if (context.Request.QueryString[key] != null)
val = context.Request.QueryString[key];
}
// Cast value
if (val != null)
result = (t)val;
return result;
}
}

could you redo that link in something like tinyurl.com?
I need this info too but can get that mega-link to work.

Related

Trying to eliminate redundant code execution for validation and entity

I'm looking to see if there is a way to eliminate one of the two calls that gets made to my method to google maps to calculate long/lat coordinates.
Here is my method.
public static GeocoderCoordinates GetCoordinates(string region)
{
WebRequest request = WebRequest.Create("http://maps.googleapis.com/maps/api/geocode/xml?sensor=false&address=" + HttpUtility.UrlEncode(region));
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
XDocument document = XDocument.Load(new StreamReader(stream));
XElement longitudeElement = document.Descendants("lng").FirstOrDefault();
XElement latitudeElement = document.Descendants("lat").FirstOrDefault();
if (longitudeElement != null && latitudeElement != null)
{
return new GeocoderCoordinates
{
Longitude = Double.Parse(longitudeElement.Value, CultureInfo.InvariantCulture),
Latitude = Double.Parse(latitudeElement.Value, CultureInfo.InvariantCulture)
};
}
}
}
return null;
}
The first time I call this method it's for validation.
internal class ValidateLocationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var location = value as string;
GeocoderCoordinates coordinates = Geocoding.GetCoordinates(location);
if (coordinates == null)
return false;
return true;
}
}
and if there is no location that gets found it returns null - validation fails.
The second time it gets called is in the controller to set longitude/latitude coordinates within my entity.
[HttpPost]
public ActionResult Edit(EditStudentViewModel viewModel)
{
if (ModelState.IsValid)
{
Student student = studentRepository.Find(User.Identity.GetUserId());
if (student == null)
{
var newStudent = new Student
{
AspNetUserRefId = viewModel.AspNetUserRefId,
CatchPhrase = viewModel.CatchPhrase,
StartedPracticing = Convert.ToInt16(viewModel.SelectedYearId),
LocationPoints = Geocoding.GetDbGeography(viewModel.Location),
Location = viewModel.Location,
SO I'm running through this method twice just to insert/update a student. It seems a little redundant.
Isn't there a way to trigger/set validation state while the code in the controller is running, so I don't have to call this method twice (once for validation and once to set the actual values) when the user submits the form?
I thought about caching but don't think it's a good idea, unless someone can point out something.
If you think applying validation upfront using an attribute on the text box serve value to the user (early feedback), keep things as it is. Two calls is not too bad at all considering the value and cleanliness of the solution.
Second option is you can remove the attribute, and perform the validation in the controller action. If validation fails, display same form with all the same data but error message for the text box value (location). User will need to choose another location and then submit.
It is a trade off.
Important Tip: You can optimize your solution by storing region names in your DB and going to google API only if the region name does not present in your DB.

FluentValidation passing parameter

I've found FluentValidation only couple of hours ago and I want to rewrite all my validation logic so it will use only FV.
The issue that I have ATM is that I would like to use data coming from input as a parameter for DomainExists() method. Is it possible or do I have to figure out a way around FV to achieve that?
public QuoteValidator()
{
// hardcoded because don't know how to pass input string to RuleFor
var inputeddomain = "http://google.com";
RuleFor(r => r.Domain).NotEqual(DomainExists(inputeddomain));
}
// checks if inputeddomain is in repository (SQL DB)
private string DomainExists(string inputeddomain)
{
var context = new QuoteDBContext().Quotes;
var output = (from v in context
where v.Domain == inputeddomain
select v.Domain).FirstOrDefault();
if (output != null) { return output; } else { return "Not found"; }
}
Thanks to #bpruitt-goddard hint I got that to work. Here's a solution to my problem (hope it will help somebody).
public QuoteValidator()
{
RuleFor(r => r.Domain).Must(DomainExists).WithMessage("{PropertyValue} exists in system!");
}
private bool DomainExists(string propertyname)
{
var context = new QuoteDBContext().Quotes;
var output = (from v in context
where v.Domain == propertyname
select v.Domain).FirstOrDefault();
if (output != null) { return false; } else { return true; }
}
You can use FluentValidation's Must method to pass in extra data from the input object.
RuleFor(r => r.Domain)
.Must((obj, domain) => DomainExists(obj.InputDomain))
.WithErrorCode("MustExist")
.WithMessage("InputDomain must exist");
Although this will work, it is not recommended to check for database existence in the validation layer as this is verification versus validation. Instead, this kind of check should be done in the business layer.

Correct way to cache images in MVC app

I am using the ImageResizing library, to resize and deliver my image in my C# Mvc application.
One thing that isn't happening though is my image aren't getting cached.
I am struggling to understand what would be required to appropriately add caching for each image.
I just need to know if I am on the write track? Will this cache my images correctly?
I think what I need to do, is set the FinalContentType, and FinalContentType in my ImageResizer_OnPostAuthorizeRequestStart (I dont know where to get these values)
And then, I am hoping that in the Application_PreSendRequestHeaders I can use the code below to set the cache headers correctly.
I have used a modified version of the method described here.
Here is my code:
private static void ImageResizer_OnPostAuthorizeRequestStart(IHttpModule sender2, HttpContext context)
{
string path = Config.Current.Pipeline.PreRewritePath;
if (!path.StartsWith(PathUtils.ResolveAppRelative("~/s3"), StringComparison.OrdinalIgnoreCase)) return;
Config.Current.Pipeline.SkipFileTypeCheck = true;
Config.Current.Pipeline.ModifiedQueryString["cache"] = ServerCacheMode.Always.ToString();
}
protected void Application_PreSendRequestHeaders(Object source, EventArgs e)
{
var app = source as HttpApplication;
HttpContext context = (app != null) ? app.Context : null;
if (context != null && context.Items != null && context.Items["FinalContentType"] != null && context.Items["LastModifiedDate"] != null)
{
//Clear previous output
//context.Response.Clear();
context.Response.ContentType = context.Items["FinalContentType"].ToString();
//FinalContentType is set to image/jpeg or whatever the image mime-type is earlier in code.
//Add caching headers
int mins = 1; //Or Configuration.AppSettings['whatever']
if (mins > 0)
{
context.Response.Expires = 1;
}
var lastModified = (DateTime?)context.Items["LastModifiedDate"]; //Set earlier in code.
if (lastModified != DateTime.MinValue)
{
Response.Cache.SetLastModified(lastModified.Value);
}
Response.Cache.SetCacheability(context.Request.IsAuthenticated ? HttpCacheability.Private : HttpCacheability.Public);
}
}
Use the DiskCache and ClientCache plugins to handle disk caching and cache headers respectively.
ASP.NET output caching is useless here.

Validating Multiple Attributes

I am building an online store for local artists, and one of the requirements is to add an image to be associated with a given product. For the image, there are multiple elements that need to be validated; specifically dimensions, file size, and type.
Currently, I have the following set up to validate the image:
[LocalizedDisplayName(typeof(StoreManagementRes), "Image")]
[ImageSize(typeof(BesLogicSharedRes),"ValidationImageFileSizeMustBeLessThan20kb")]
[ImageDimension(typeof(BesLogicSharedRes), "ValidationImageDimensionMustBeLessThan640x480")]
[ImageType(typeof(BesLogicSharedRes), "ValidationImageTypeMustBeJpgOrPng")]
public int ImageFileId { get; set; }
The file that is uploaded does get validated properly, however, they are not necessarily called in the same order every time the application runs. In the end, if validation fails on more than one attribute, only one error message gets displayed. Again, not necessarily the first failed validation, nor the last. I would like to display all the errors at once so as not to frustrate the user.
If this is relevant, all three image validation classes are sub classed from ValidationAttribute.
One thing to be thankful of is that the model keeps all errors rather than one of them, it's just the HtmlHelper that's displaying the first.
ValidationSummary should in fact display all errors on your model though I suspect you want the equivalent for an individual property.
Unfortunately a couple of the useful methods are private rather than protected so they had to be copy and pasted out of ValidationExtensions.cs. I did this with a slightly cut down version (no use of resource files for error messages, easy enough to do by getting the original version of GetUserErrorMessageOrDefault but you'll also have to check to take the related methods and fields from the class too). I also only did one function call but it's easy enough to impliment the overloads if needed.
public static MvcHtmlString ValidationSummaryForSubModel(this HtmlHelper html, bool excludePropertyErrors, string message, IDictionary<string, object> htmlAttributes)
{
string prefix = html.ViewData.TemplateInfo.HtmlFieldPrefix;
var props = html.ViewData.ModelState.Where(x => x.Key.StartsWith(prefix));
var errorprops = props.Where(x => x.Value.Errors.Any()).SelectMany(x=>x.Value.Errors);
if (html == null) {
throw new ArgumentNullException("html");
}
FormContext formContext = (html.ViewContext.ClientValidationEnabled) ? html.ViewContext.FormContext : null;
if (formContext == null && html.ValidForSubModel())
{
return null;
}
string messageSpan;
if (!String.IsNullOrEmpty(message)) {
TagBuilder spanTag = new TagBuilder("span");
spanTag.SetInnerText(message);
messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else {
messageSpan = null;
}
StringBuilder htmlSummary = new StringBuilder();
TagBuilder unorderedList = new TagBuilder("ul");
foreach (ModelError modelError in errorprops) {
string errorText = GetUserErrorMessageOrDefault(html.ViewContext.HttpContext, modelError, null /* modelState */);
if (!String.IsNullOrEmpty(errorText)) {
TagBuilder listItem = new TagBuilder("li");
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
if (htmlSummary.Length == 0) {
htmlSummary.AppendLine(_hiddenListItem);
}
unorderedList.InnerHtml = htmlSummary.ToString();
TagBuilder divBuilder = new TagBuilder("div");
divBuilder.MergeAttributes(htmlAttributes);
divBuilder.AddCssClass((html.ViewData.ModelState.IsValid) ? HtmlHelper.ValidationSummaryValidCssClassName : HtmlHelper.ValidationSummaryCssClassName);
divBuilder.InnerHtml = messageSpan + unorderedList.ToString(TagRenderMode.Normal);
if (formContext != null) {
// client val summaries need an ID
divBuilder.GenerateId("validationSummary");
formContext.ValidationSummaryId = divBuilder.Attributes["id"];
formContext.ReplaceValidationSummary = !excludePropertyErrors;
}
return MvcHtmlString.Create(divBuilder.ToString(TagRenderMode.Normal));
}
private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
{
if (!String.IsNullOrEmpty(error.ErrorMessage))
{
return error.ErrorMessage;
}
if (modelState == null)
{
return null;
}
string attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : null;
//return String.Format(CultureInfo.CurrentCulture, GetInvalidPropertyValueResource(httpContext), attemptedValue);
return "Error";
}

Does asp.net MVC have Application variables?

I am busy converting a web application to MVC and have some information saved to Application variables used across multiple tenants/accounts to make things a bit more efficient.
I realise the point of MVC is to keep things as stateless as possible, Sesion State obviously makes sense to have and exists in MVC but we dont want to just convert Application to Session variables as we would rather have something more global and more secure. Do MVC applications have Application Variables? I have seen some examples where caching is used? Is this now standard and How robust/secure is this compared to Application/Session State?
Yes, you can access Application variables from .NET MVC. Here's how:
System.Web.HttpContext.Current.Application.Lock();
System.Web.HttpContext.Current.Application["Name"] = "Value";
System.Web.HttpContext.Current.Application.UnLock();
Session state or the Cache are better choices. They are mockable in MVC and are designed to store session and application-scoped data.
Static classes seems like a popular choice here. However static classes create dependencies between your types and make versioning/testing harder. Its also a bit of an odd pattern to use in a framework that is designed to break apart these kinds of dependencies. For instance, the standard ASP.NET framework is riddled with statics and sealed types. These are all replaced with mock-able instances.
"Secure" is a bit unclear in this context. Exactly what do you mean by "secure?"
I implemented something like below as an Extension for Global state variable. I put things like Site title,Service Endpoints, authorized roles
public static class ApplicationStateExtension
{
public static T GetSetApplicationState<T>(this HttpApplicationState appState, string objectName, object objectValue = null, int syncCheckMinutes = 0)
{
T retVal = default(T);
appState.Lock();
if (appState[objectName + "LastSync"] == null || DateTime.Now.Subtract(((DateTime)appState[objectName + "LastSync"])).TotalMinutes >= syncCheckMinutes)
{
appState[objectName + "LastSync"] = DateTime.Now;
if (objectValue != null)
appState[objectName] = objectValue;
}
if (appState[objectName] != null)
retVal = (T)appState[objectName];
appState.UnLock();
return retVal;
}
public static object GetSetApplicationState(this HttpApplicationState appState, string objectName, object objectValue = null, int syncCheckMinutes = 0)
{
object retVal = null;
appState.Lock();
if (appState[objectName + "LastSync"] == null || DateTime.Now.Subtract(((DateTime)appState[objectName + "LastSync"])).TotalMinutes >= syncCheckMinutes)
{
appState[objectName + "LastSync"] = DateTime.Now;
if (objectValue != null)
appState[objectName] = objectValue;
}
if (appState[objectName] != null)
retVal = appState[objectName];
appState.UnLock();
return retVal;
}
public static void SetApplicationState(this HttpApplicationState appState, string objectName, object objectValue, int syncCheckMinutes = 0)
{
appState.Lock();
if (appState[objectName + "LastSync"] == null || DateTime.Now.Subtract(((DateTime)appState[objectName + "LastSync"])).TotalMinutes >= syncCheckMinutes)
{
appState[objectName + "LastSync"] = DateTime.Now;
appState[objectName] = objectValue;
}
appState.UnLock();
}
public static object GetApplicationState(this HttpApplicationState appState, string objectName)
{
object retVal = null;
appState.Lock();
if (appState[objectName] != null)
retVal = appState[objectName];
appState.UnLock();
return retVal;
}
public static T GetApplicationState<T>(this HttpApplicationState appState, string objectName)
{
T retVal = default(T);
appState.Lock();
if (appState[objectName] != null)
retVal = (T)appState[objectName];
appState.UnLock();
return retVal;
}
}
So I can set them from Global.asax.cs something like this
Application.SetApplicationState("UISiteTitle",paramHelper.GetUIConfigXML<XMLParams.UISiteOptions>("UISiteOptions")
.SiteOptionCollection.Where(v => v.name.Equals("title", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault().value););
or
var uiPermissions = Application.GetSetApplicationState<XMLParams.UIPermissions>("UIPermissions", paramHelper.GetUIConfigXML<XMLParams.UIPermissions>("UIPermissions"), 30);
You can declare Application variables in Application_Start like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
var e = "Hello";
Application["value"] = e;
}
To access this on controller write:
string appVar = HttpContext.Application["value"] as string;
Make a static class?
Do they have Application Variables? Yes, MVC is a framework that sits on top of the normal asp.net framework.
I would however create a static class that uses a cache store as it's backing.

Categories

Resources