Specifyng a default message for Html.ValidationMessageFor in ASP.NET MVC4 - asp.net-mvc-4

I want to display an asterisk (*) next to a text box in my form when initially displayed (GET)
Also I want to use the same view for GET/POST when errors are present) so For the GET request
I pass in an empty model such as
return View(new Person());
Later, when the form is submitted (POST), I use the data annotations, check the model state and
display the errors if any
Html.ValidationMessageFor(v => v.FirstName)
For GET request, the model state is valid and no messages, so no asterisk gets displayed.
I am trying to workaround this by checking the request type and just print asterisk.
#(HttpContext.Current.Request.HttpMethod == "GET"? "*" : Html.ValidationMessageFor(v=> v.FirstName).ToString())
The problem is that Html.ValidationMessageFor(v=> v.FirstName).ToString() is already encoded
and I want to get the raw html from Html.ValidationMessageFor(v=> v.FirstName)
Or may be there is a better way here.
1. How do you display default helpful messages (next to form fields) - such as "Please enter IP address in the nnn.nnn.nnn.nnn format) for GET requests and then display the errors if any for the post?
2. What is the best way from a razor perspective to check an if condition and write a string or the MvcHtmlString

Further to my last comment, here is how I would create that helper to be used:
public static class HtmlValidationExtensions
{
public static MvcHtmlString ValidationMessageForCustom<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string customString)
{
var returnedString = HttpContext.Current.Request.HttpMethod == "GET" ? customString : helper.ValidationMessageFor(expression).ToString();
return MvcHtmlString.Create(returnedString);
}
}
And it would be used like this #Html.ValidationMessageForCustom(v=> v.FirstName, "Please enter IP address in the nnn.nnn.nnn.nnn format")

Related

Use specific status code error page in area | Asp.net core

I manage status codes error using
app.UseStatusCodePagesWithRedirects("/StatusCodeError/{0}");
But when an error occurs in the area, the page is redirected out of the area and shows the general error page.
How do I make sure that an area-specific error page is displayed if an error occurs in the area?
There is no extensibility point to modify the behavior of app.UseStatusCodePagesWithRedirects, the location is formatted internally using string.Format and with only one argument passed in the placeholder {0} for the status code. So at the calling time (passing in the location format), we have no understanding about the area which is a route value available only in the context of a request processing. So you can refer to the source for StatusCodePagesExtensions and can see that UseStatusCodePagesWithRedirects just depends on an overload of the extension method StatusCodePagesExtensions.UseStatusCodePages, you can freely copy the code and modify it to make another version of UseStatusCodePagesWithRedirects that accepts a location format which supports some arguments from the route values.
Here I've made a simple one for you:
public static class StatusCodePagesApplicationBuilderExtensions
{
public static IApplicationBuilder UseStatusCodePagesWithRouteDataAndRedirects(this IApplicationBuilder app, string locationFormat)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var shouldPrependPathBase = locationFormat.StartsWith("~");
locationFormat = shouldPrependPathBase ? locationFormat.Substring(1) : locationFormat;
return app.UseStatusCodePages(context =>
{
//here is the point we can evaluate for the actual location
//by replacing the placeholders with route value
var location = Regex.Replace(locationFormat, #"\{[^}]+\}", m => {
//ignore the placeholder for status code
if (m.Value == "{0}") return m.Value;
var routeKey = m.Value.Trim('{', '}');
var routeValue = context.HttpContext.GetRouteValue(routeKey);
return routeValue?.ToString();
});
location = string.Format(CultureInfo.InvariantCulture, location, context.HttpContext.Response.StatusCode);
location = (shouldPrependPathBase ? context.HttpContext.Request.PathBase : PathString.Empty) + location;
context.HttpContext.Response.Redirect(location);
return Task.CompletedTask;
});
}
}
Now you can include any route data in the location format, in your case you just need the area, so the code will be like this:
app.UseStatusCodePagesWithRouteDataAndRedirects("/{area}/StatusCodeError/{0}");
NOTE: be careful with redirecting, you may encounter an error saying something like too many redirects, that's because of redirecting to a URL which in return causes a loop of redirects.

RazorPages anchor tag helper with multiple parameters

Here's the RazorPages page I'm trying to make a link to:
#page "{ReportId:int}/{SicCode:alpha?}"
This works
<a asp-page="/ReportSics" asp-route-ReportId="3">rs1</a>
it produces
rs1
But this produces a blank href.
<a asp-page="/ReportSics" asp-route-ReportId="3" asp-route-SicCode="10">rss2</a>
That is: the tag helper works with one parameter but not with two.
Why?
Is it possible to make it work?
(I have another page with the same #page but with the second parameter not optional and it appears to be impossible to create a link to it.)
Furthermore, requesting Page/2/M works, but Page/2/12 returns 404. Why? (The second parameter is a string that can sometimes be a number, but it always treated as a string.)
From the learn.microsoft.com webpage asp-all-route-data offers the following:
asp-all-route-data
The asp-all-route-data attribute supports the creation of a dictionary of key-value pairs. The key is the parameter name, and the value is the parameter value.
In the following example, a dictionary is initialized and passed to a Razor view. Alternatively, the data could be passed in with your model.
#{
var parms = new Dictionary<string, string>
{
{ "speakerId", "11" },
{ "currentYear", "true" }
};
}
<a asp-route="speakerevalscurrent"
asp-all-route-data="parms">Speaker Evaluations</a>
The preceding code generates the following HTML:
Speaker Evaluations
Extension: From here the parameters can be accessed either explicitly in the parameter list of the method:
public IActionResult EvaluationCurrent(int speakerId, string currentYear)
or as a collection (See response: queryString:
public IActionResult EvaluationCurrent()
{
var queryString = this.Request.Query;
}
This works
Yes it works because it produces a route that is similar to this baseUrl/reportsics/?reportId=5
And the other produces a URL that is similar to this baseUrl/reportsics/?reportId=5&sicCode=678 and then it doesn't match your route definition. I think you should try this.
Experimental
asp-page="/reportSics/#myId/#sicCode
Though this would not be the right way to do what you're thinking. If you really want to change your URL structure, why not do url-rewrite?
Edit.
Form your recent comments, seems you want to pass many parameters in your action method and not targeting URL structure. Then I recommend you just
public IActionResult(string ReportId, string sicCode)
{
//......
}
//And the your URL target
<a asp-page="ReportSics" asp-route-ReportId="55" asp-route-sicCode="566" ></a>
And then it will match the route. I think you should remove that helper you placed after your #page definition and try it out if this is what you have already done and the problem persists.
It turns out that if a parameter has the constraint :alpha then it only works if the value being passed can not be parsed as an int or float.

How to get current url in view in asp.net core 1.0

In previous versions of asp.net, we could use
#Request.Url.AbsoluteUri
But it seems it's changed. How can we do that in asp.net core 1.0?
You have to get the host and path separately.
#Context.Request.Host
#Context.Request.Path
You need scheme, host, path and queryString
#string.Format("{0}://{1}{2}{3}", Context.Request.Scheme, Context.Request.Host, Context.Request.Path, Context.Request.QueryString)
or using new C#6 feature "String interpolation"
#($"{Context.Request.Scheme}://{Context.Request.Host}{Context.Request.Path}{Context.Request.QueryString}")
You can use the extension method of Request:
Request.GetDisplayUrl()
This was apparently always possible in .net core 1.0 with Microsoft.AspNetCore.Http.Extensions, which adds extension to HttpRequest to get full URL; GetEncodedUrl.
e.g. from razor view:
#using Microsoft.AspNetCore.Http.Extensions
...
Link to myself
Since 2.0, also have relative path and query GetEncodedPathAndQuery.
Use the AbsoluteUri property of the Uri, with .Net core you have to build the Uri from request like this,
var location = new Uri($"{Request.Scheme}://{Request.Host}{Request.Path}{Request.QueryString}");
var url = location.AbsoluteUri;
e.g. if the request url is 'http://www.contoso.com/catalog/shownew.htm?date=today' this will return the same url.
You can consider to use this extension method (from Microsoft.AspNetCore.Http.Extensions namespace:
#Context.Request.GetDisplayUrl()
For some my projects i prefer more flexible solution. There are two extensions methods.
1) First method creates Uri object from incoming request data (with some variants through optional parameters).
2) Second method receives Uri object and returns string in following format (with no trailing slash): Scheme_Host_Port
public static Uri GetUri(this HttpRequest request, bool addPath = true, bool addQuery = true)
{
var uriBuilder = new UriBuilder
{
Scheme = request.Scheme,
Host = request.Host.Host,
Port = request.Host.Port.GetValueOrDefault(80),
Path = addPath ? request.Path.ToString() : default(string),
Query = addQuery ? request.QueryString.ToString() : default(string)
};
return uriBuilder.Uri;
}
public static string HostWithNoSlash(this Uri uri)
{
return uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.UriEscaped);
}
Usage:
//before >> https://localhost:44304/information/about?param1=a&param2=b
Request.GetUri(addQuery: false);
//after >> https://localhost:44304/information/about
//before >> https://localhost:44304/information/about?param1=a&param2=b
new Uri("https://localhost:44304/information/about?param1=a&param2=b").GetHostWithNoSlash();
//after >> https://localhost:44304
There is a clean way to get the current URL from a Razor page or PageModel class. That is:
Url.PageLink()
Please note that I meant, the "ASP.NET Core Razor Pages", not the MVC.
I use this method when I want to print the canonical URL meta tag in the ASP.NET Core razor pages. But there is a catch. It will give you the URL which is supposed to be the right URL for that page. Let me explain.
Say, you have defined a route named "id" for your page and therefore, your URL should look like
http://example.com/product?id=34
The Url.PageLink() will give you exactly that URL as shown above.
Now, if the user adds anything extra on that URL, say,
http://example.com/product?id=34&somethingElse
Then, you will not get that "somethingElse" from this method. And that is why it is exactly good for printing canonical URL meta tag in the HTML page.
The accepted answer helped me, as did the comment for it from #padigan but if you want to include the query-string parameters as was the case for me then try this:
#Context.Request.PathBase#Context.Request.GetEncodedPathAndQuery()
And you will need to add #using Microsoft.AspNetCore.Http.Extensions in the view in order for the GetEncodedPathAndQuery() method to be available.
public string BuildAbsolute(PathString path, QueryString query = default(QueryString), FragmentString fragment = default(FragmentString))
{
var rq = httpContext.Request;
return Microsoft.AspNetCore.Http.Extensions.UriHelper.BuildAbsolute(rq.Scheme, rq.Host, rq.PathBase, path, query, fragment);
}
If you're looking to also get the port number out of the request you'll need to access it through the Request.Host property for AspNet Core.
The Request.Host property is not simply a string but, instead, an object that holds both the host domain and the port number. If the port number is specifically written out in the URL (i.e. "https://example.com:8080/path/to/resource"), then calling Request.Host will give you the host domain and the port number like so: "example.com:8080".
If you only want the value for the host domain or only want the value for the port number then you can access those properties individually (i.e. Request.Host.Host or Request.Host.Port).
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}{Context.Request.QueryString}";
You may want to get the URL to use it on the razor side, there is an alternative way to get the home app URL:
Url.Content("~/.....")
Example
In the following example, I wanted to generate a QR code and display it in an img tag src, because of using custom route annotation in the Action, I can't use #URL.Action so as an alternative solution I use ~ like this:
<script>
$("#imgCode").attr("src", "#(Url.Content("~/generateQr/"))"+ code);
</script>
Controller Side
[Route("/generateQr/{code}")]
...
ILSpy show how it was done in Microsoft.Owin.dll.
// Microsoft.Owin.OwinRequest
using System;
/// <summary>
/// Gets the uniform resource identifier (URI) associated with the request.
/// </summary>
/// <returns>The uniform resource identifier (URI) associated with the request.</returns>
public virtual Uri Uri => new Uri(string.Concat(Scheme, Uri.SchemeDelimiter, Host, PathBase, Path, QueryString));
I wonder why they removed this property.

Modifying Error message at Client side. Custom Validation ASP .NET MVC

I am trying implement custom client side validation.It is a cross coupled validation. I have followed all steps and its working fine. But my requirement requires me to modify the ErrorMessage which is will be part of metadata(HTML 5 Data Attribute). The example is as follows
IClientValidatable implementation:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule();
rule.ValidationType = "salaryandloanconstraint";
rule.ValidationParameters.Add("loanconstraintvalue", _loanEligibleMultiplicity);
rule.ErrorMessage = "Salary Insufficient to sanction a loan amount of";
return new ModelClientValidationRule[] { rule };
}
The message which i have initilized to rule.ErrorMessage is incomplete. I want to append text which is taken from input field for LoanAmount property to the error message when the user enters.
In Summary is there any way which i can manipulate the error message(HTML5 DATA ATTRIBUTES) at the client side using JQuery?
check this url, may be this solve your issue:
$(".field-validation-error span", $("#form"))
.clone()
.appendTo("#error-summary")
.wrap("<li>");

An interesting Restlet Attribute behavior

Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.