How does cascaded parameter Task<AthenticationState> get unwrapped and exposed as "context" in AuthorizeView and AuthorizedRouteView in Blazor WASM? - asp.net-core

There is a an object of type AuthenticationState named "context" that is available inside AuthorizeView and AuthorizedRouteView components. This object allows to access the ClaimsPrincipal via context.User.
I can see in the source code that AuthenticationState is passed down to these components as Task by the CascadingValue component implemented in the CascadingAuthenticationState component (which in turn is defined at the top of the component hierarchy in App.razor).
However, when I inspect the source of the AuthorizeRouteView I can see the cascading parameter of type Task named ExistingCascadedAuthenticationState. Yet, it is a complete mystery to me how and where does the Task gets unwrapped and exposed as "context". Does anyone know the answer?

You need to dig deep, and it's a little complicated.
AuthorizeView inherits from AuthorizeViewCore
AuthorizedRouteView builds it's own AuthorizeRouteViewCore inheriting from AuthorizeViewCore.
Code at the bottom of AuthorizedRouteView:
private sealed class AuthorizeRouteViewCore : AuthorizeViewCore
{
[Parameter]
public RouteData RouteData { get; set; } = default!;
protected override IAuthorizeData[]? GetAuthorizeData()
=> AttributeAuthorizeDataCache.GetAuthorizeDataForType(RouteData.PageType);
}
AuthorizedRouteView captures any cascade into ExistingCascadedAuthenticationState. If one exists (not null) then CascadingAuthenticationState exists in App, so nothing more needs doing. If it's null then it adds CascadingAuthenticationState as the component root component into its render fragment. This guarantees that Task<AuthenticationState> is cascaded.
AuthorizeViewCore captures the cascaded value:
[CascadingParameter] private Task<AuthenticationState>? AuthenticationState { get; set; }
It gets "unwrapped" in OnParametersSetAsync
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);
and used in BuildRenderTree to the "context" you see.
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState!));
The content comes from:
RenderFragment<TValue>
declared as follows where TValue- content - is declared as AuthenticationState :
[Parameter] public RenderFragment<AuthenticationState>? Authorized { get; set; }

The comment of enet helped me to find the answer.
When we have a RenderFragment<TValue> delegate, the <TValue> is exposed by default as #context.
For example, in AuthorizeRouteView we have a parameter NotAuthorized:
[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
In this case AuthenticationState is TValue, therefore AuthenticationState is exposed as #context.
This article on Blazor University was the key for me to get the concept: Passing placeholders to RenderFragments.
Edit 2022-05-29
The recently added answer of #somedotnetguy makes it even more clear how render templates work. I suggest also reading his answer to get a more complete picture.

I was wondering the exact same thing:
How come, that suddenly inside the inner markup of a component (between its opening and closing tag, when its consumed in a parent) we can write #context and where does the value come from?
The provided answers helped me to figure it out and there is good explanaition on Blazor University - RenderFragements (entire chapter, this page and the 4 following)
As seen in Official Docs here and here the AuthorizeView has a property ChildContent of type RenderFragment<AuthenticationState> decorated with Parameter.
[Microsoft.AspNetCore.Components.Parameter]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.Authorization.AuthenticationState>?
ChildContent { get; set; }
Here I go:
You cannot do this with a plain (empty, newly created) component:
//Parent.razor consumes MyComp
<MyComp>Some markup as content for MyComp</MyComp>
The FRAMEWORK CONVENTION is, you need the following code inside the component (class) definition, to enable the possibility to write markup between the tags (when it is consumed). The markup then gets passed as the ChildComponent property of type RenderFragment:
//MyComp.razor
[Parameter]
public RenderFragment ChildContent { get; set; }
You can also use properties/fields of the type RenderFragment or RenderFragment with other property-names, but they won't get automatically filled from the parent like above. However you can use them inside your component definition as you please. To set the content in the consuming parents of any RenderFragment you use its property name like an html tag and write the razor content inside it. The exception here is, if you ONLY want to fill the ChildContent and don't use other RenderFragments, it can be omitted like above (since this is a special name).
//MyComp.razor
<p>Fragment1:</p>
#SomeOtherRenderFragment
<p>Fragment2:</p>
#SomeGenericRenderFragment(_instanceT)
#code {
[Parameter]
public RenderFragment SomeOtherRenderFragment{ get; set; } =
#<h1>My Render Fragment Example</h1>;
[Parameter]
public RenderFragment<T> SomeGenericRenderFragment{ get; set; } // not set here in this example
private T _instanceT = null // T must be an explicit type, it's a placeholder in this example... for instance change it to 'string'. You can get the instance from where ever you want, probably through some service that you inject with DI
}
//Parent.razor consumes MyComp
// Implicit setting ChildContent
<MyComp>Some markup as content for MyComp</MyComp>
// Explicit setting ChildContent
<MyComp><ChildContent>Some markup as content for MyComp</ChildContent></MyComp>
// Explicit setting various RenderFragments
<MyComp>
<ChildContent>Some markup as content for MyComp</ChildContent>
<SomeOtherRenderFragment>SomeContent</SomeOtherRenderFragment>
<SomeGenericRenderFragment>SomeContent with #context</SomeGenericRenderFragment>
</MyComp>
And now putting it all together. You can also use the generic RenderFragment as type for the convention-property ChildContent. However, it is your responsibility to provide an instance of the generic class inside the component definition.
//MyComp.razor
<p>Content passed from parent:</p>
#ChildContent(_instanceT1)
<p>Content passed from parent a second time:</p>
#ChildContent(_instanceT2)
#code {
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
private string _instanceT1 = "Hello World!";
private string _instanceT2 = "Good Night!";
}
Parent:
//Parent.razor
<h1>Parent:</h1>
<MyComp>
<p>I can type content here and now comes the value of T: #context</p>
<p>and again again: #context</p>
</MyComp>
Final Html will look sth like this:
<h1>Parent:</h1>
<p>Content passed from parent:</p>
<p>I can type content here and now comes the value of T: Hello World!</p>
<p>and again again: Hello World!</p>
<p>Content passed from parent a second time:</p>
<p>I can type content here and now comes the value of T: Good Night!</p>
<p>and again again: Good Night!</p>
Note: #context is ONLY available inside a component's tag (when consumed), if it has this generic RenderFragment property in its definition:
//MyComp.razor
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
AuthorizeView can use RenderFragments:
ChildContent (most often used implicit, also explicit possible)
Authorized (explicit)
NotAuthorized (explicit)
However it is implemented in such a way, that an exception is thrown if both are specified: Unhandled exception rendering component: Do not specify both 'Authorized' and 'ChildContent'. Basically Authorized substitutes ChildContent, or in other words, when not setting any RenderFragment explicitly, the ChildContent gets treated like Authorized, like shown in the answer by MrC aka Shaun Curtis.
Final Words: I hope this helps and I hope I kept typos to a minimum :)

The cascading parameter is Task<AuthenticationState> context
Which tells you that context, when awaited, will return the object of type AuthenticationState. So what you get is a Task. The task when awaited, returns the value returned by the Task. The actual syntax for accessing the User is
var state = await context;
var user = state.User;
Also, you can give any name to the cascading parameter. So
[CascadingParameter]
public Task<AuthenticationState> AuthState {get;set;}
var state = await AuthState;
var user = state.User;
is equally valid.

Related

asp.net core api - how to distinguish between `api/cars` and `api/cars?color=red` calls with [FromQuery] object

I am using [FromQuery] atribute in controller's Get method:
//CarsController, etc..
[HttpGet]
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery] CarsParameter? carsParam = null)
{
//param is always not null here
}
Inside the method I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null, so I cannot say if the Color="" (defailt value) is intended to be empty string or it's because of the call was api/cars
CarsParameter is a simple class:
public class CarsParameter
{
public string Color {get; set;} = "";
//more params here
}
Yes, I can use different path, like api/cars/withParams?color=red, but i am looking for more subtle solution.
I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null
Please note that default model binding starts by looking through the sources for the key carsParam.Color. If that isn't found, it looks for Color without a prefix, which cause the issue.
To achieve your requirement, you can try to specify prefix explicitly, like below.
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery][Bind(Prefix = "carsParam")] CarsParameter? carsParam = null)
{
Request to api/cars?color=red&carsParam.color=yellow&carsParam.brand=test and following is test result

Instantiating ModelExpression directly

Let's say I have the following input tag which utilizes the built-in tag helper:
#model ProductViewModel
<label asp-for="Product.Id"></label>
In my case, this expands into the following:
<label for="Product_Id">Id</label>
I see that asp-for is expecting a ModelExpression:
In tag helper implementations, I often see a property like the following:
public ModelExpression For { get; set; }
It appears that this is automatically populated when the tag helper is used.
Is there a way to instantiate a ModelExpression directly in C#?
I.e. something like this:
var exp = new ModelExpression("Product.Id",...)
I'd like to be able to generate "Product_Id" and "Id" from Product.Id as the input tag helper did.
As far as I know, you can specify that your property is to be set to the name of some property on the View's Model object by declaring your property with the ModelExpression type. This will enable any developer using your property to get IntelliSense support for entering a property name from the Model object. More importantly, your code will be passed the value of that property through the ModelExpression's Model property.
Sample code as below:
[HtmlTargetElement("employee-details")]
public class EmployeeDetailTagHelper : TagHelper
{
[HtmlAttributeName("for-name")]
public ModelExpression EmployeeName { get; set; }
[HtmlAttributeName("for-designation")]
public ModelExpression Designation { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "EmployeeDetails";
output.TagMode = TagMode.StartTagAndEndTag;
var sb = new StringBuilder();
sb.AppendFormat("<span>Name: {0}</span> <br/>", this.EmployeeName.Model);
sb.AppendFormat("<span>Designation: {0}</span>", this.Designation.Model);
output.PreContent.SetHtmlContent(sb.ToString());
}
}
Code in the View page:
#model WebApplication7.Models.EmployeeViewModel
<div class="row">
<employee-details for-name="Name" for-designation="Designation"></employee-details>
</div>
Code in the Model
public class EmployeeViewModel
{
public string Name { get; set; }
public string Designation { get; set; }
}
From above code, you can see that we could custom the attribute name. More detail information about using the ModelExpression, check the following links:
Creating Custom Tag Helpers With ASP.NET Core MVC
Expression names
I'd like to be able to generate "Product_Id" and "Id" from Product.Id
as the input tag helper did.
Besides, do you mean you want to change the Product. Id to Product_Id, in my opinion, I'm not suggesting you change it, because generally we can use "_" as a separator in the property name. So, if we are using Product.Id, it means the Product's Id property, and the Product_Id means there have a Product_Id property.
To answer the question:
Is there a way to instantiate a ModelExpression directly in C#"
Yes you can, through IModelExpressionProvider and its CreateModelExpression method. You can get an instance of this interface through DI.
Now, if you're already in your view and working with tag helpers, Zhi Lv's answer is all you need, as the functionality is built-in and much easier to use. You only need IModelExpressionProvider for when you're in your Razor Page, Controller, or perhaps some custom middleware. Personally, I find this functionality useful for my Ajax handlers that need to return one of my ViewComponents that has a ModelExpression argument (so that I can easily call it from my Pages/Views too.)
To call CreateModelExpression, you'll need a strongly-typed instance of ViewData. In Razor Pages, this is as easy as casting the ViewData property to the strongly-typed instance of your PageModel's type (presuming you don't have a page model hierarchy):
var viewData = (ViewDataDictionary<IndexModel>)ViewData;
If you're using MVC and you're in the controller, that won't exist yet. Best you can do is make your own instance.
var viewData = new ViewDataDictionary<ErrorViewModel>(new EmptyModelMetadataProvider(),
new ModelStateDictionary());
Once you get your strongly-typed ViewData instance, you can obtain your desired ModelExpression like this, just like using a lambda expression in your views:
var myPropertyEx = _modelExpressionProvider.CreateModelExpression(viewData,
m => m.MyProperty);

How to bind dynamic complex objects created using partial-view to a collection property in view-model

I'm unable to bind a collection of child-complext objects created dynamically using a partial-view to view-model IEnumerable property.
I have successfully bound objects created dynamically using partial-views to a view-model using a technique I found on this blog https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/. I have followed the same technique but I'm unable to bind a collection to a IEnumerable property in a view-model.
[BindRequired]
public class EmployeeViewModel
{
other properties....
public IEnumerable<ContactDetailViewModel> EmployeeContact { get; set; }
}
[BindRequired]
public class ContactDetailViewModel
{
// I use this as my indexer for dynamic elements
public string RecordId { get; set; } = Guid.NewGuid().ToString();
public string Telephone { get; set; }
public string EmailAddress { get; set; }
public string ContactDescription { get; set; }
}
I call into this action-method via ajax to add dynamic contact detail elements and it returns the partial-view as html and it works fine.
[Route("[action]", Name = "BlankEmployeeContactDetail"), HttpGet("AddBlankContactDetail")]
public PartialViewResult AddBlankContactDetail()
{
return PartialView("_ContactInformation", new ContactDetailViewModel());
}
The initial contact detail is added to the main-view using the following, kindly follow this link https://1drv.ms/u/s!AkRSHVUtFlKhuHaxH96Ik4ineATE to download the main view and partial-view cshtml files. It is also noteworthy to mention that model binding fails for all other properties when I include this partial-view but works when I comment it out. I'm baffled and would greatly appreciate any help you can afford me.
<section id="widget-grid" class="">
<div class="row contactContainer">
#{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel()); }
</div>
</section>
This is the controller action method I'm trying to bind posted data to:
[Route("[action]"), HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public IActionResult Register([FromForm] EmployeeViewModel model, [FromQuery] string returnUrl = null)
{
if (ModelState.IsValid)
{
}
return View(model);
}
In order to bind, the input names much follow a particular convention that maps to what you're binding to. While it's unclear from your question, my best guess is that you're trying to ultimately bind to an instance of EmployeeViewModel, which means that your contact information inputs would need names like: EmployeeContact[0].Telephone, but when you pass an instance of ContactDetailViewModel along as the "model" of the partial view, the names will be just Telephone, and worse, these same names will be repeated over and over, i.e. each contact information set of fields you create will all have an input named just Telephone.
Long and short, you need the context of the whole model to generate the correct input names. You have a couple of options.
Since you're retrieving the set of fields via an AJAX request, it would be possible to pass the "prefix" to use along with that request. In other words, you can keep track of an index value, counting how many of these sections you've added, and then send along with the request for a new section something like
prefix: 'EmployeeContact[' + (i + 1) + ']',
Then, in your partial view:
#{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel(), new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = ViewBag.Prefix } } ); }
That's a little hacky, and honestly probably rather prone to error, though. The better option would be to take an entirely different approach. Instead of calling back to get the partial view, define it just once as a template:
<script type="text/html" id="ContactInformationTemplate">
<!-- HTML for contact information inputs -->
</script>
Then, using a library like Vue, React, Angular, etc., you can set up a "foreach" construction tied to a particular JavaScript array which uses this template to render items in that array. Then, adding a new set of inputs is as simple as adding a new item to the array. You will have to do some works to customize the input names based on the index of the item in the array, but all of these client-side frameworks have ways to do that. That would also have the side benefit of not having to make an AJAX request every time you want to add a new section.

ChildTag appears outside parentTag in TagHelper

I have Parent and Child TagHelper.. I have set attribute "parentTag" for Child Tag and yet it displays outside that parent Tag. How to restrict it from appearing outside parent Tag
[HtmlTargetElement("TestParent")]
public class Parent: TagHelper{
}
[HtmlTargetElement("TestChild",ParentTag = "TestParent")]
public class Child : TagHelper
{
}
What it need is that "TestChild" should not appear outside "TestParent"
For example:
<TestChild value=2></TestChild>
Above code must throw error or should not be shown in intellisense as it not enclosed by parent Tag "Testparent"
I tried to recreate your scenario with parent tag helper
[HtmlTargetElement("TestParent")]
public class ParentTagHelper : TagHelper
{
}
and a child tag helper:
[HtmlTargetElement("TestChild", ParentTag = "TestParent")]
public class ChildTagHelper : TagHelper
{
public int Value { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Content.SetContent(Value.ToString());
}
}
I added those two in _ViewImports.cshtml, then build the project. Then i called the tag helpers in a view:
<TestParent>
<TestChild value="101"></TestChild>
</TestParent>
<TestChild value="202"></TestChild>
I saw expected IntelliSense on first tag and didn't see one on the other one:
Finally, I executed the web app on ASP.NET Core 2.2. The HTML code of resulted page was:
<TestParent>
<TestChild>101</TestChild>
</TestParent>
<TestChild value="202"></TestChild>
It looks perfectly fine. Just as expected. The first tag was processed, the second one wasn't.
Conclusion
It looks like you had other problems aside from those described in original question. Most likely the problem wasn't in the sole tag helpers functionality and their restrictions on parent tags.

MVC 4 Validation Attribute is not working for dynamically added fields

Here are my Product and ProductItem classes/models:
public class Product
{
public int ProductId { get; set; }
[Required(ErrorMessage="Enter Name")]
public string Name { get; set; }
public List<ProductItem> productitems { get; set; }
[Required(ErrorMessage="Enter Price")]
public decimal Price { get; set; }
}
public class ProductItem
{
[Required(ErrorMessage="Select Raw Material")]
public int RawMaterial { get; set; }
[Required(ErrorMessage="Enter Quantity")]
public decimal Qty { get; set; }
}
For ProductItem I am adding its fields dynamically with jQuery, as you can see here:
$("#btnAddProductItem").click(function () {
$.getJSON("/rawmaterial/GetRawMaterials", null, function (data) {
var productItem = $("<tr class='productItem' id='productItem-0'><td><select id='rmlist-0' name='productitems[0].RawMaterial'></select><span class='field-validation-valid' data-valmsg-for='productitems[0].RawMaterial' data-valmsg-replace='true'></span></td><td><input type='text' id='rmqty-0' name='productitems[0].Qty'/><span class='field-validation-valid' data-valmsg-for='productitems[0].Qty' data-valmsg-replace='true'></span></td></tr>");
$("#productItem").append(productItem);
$("#rmlist-0").addItems(data);
});
});
Now the validation attributes applied on Name and Price are working fine but not on the fields added dynamically (i.e. "RawMaterial" and "Qty").
Please give me the suggestions how this validation will work ?
Note: For testing purpose I have just added the first object of the List indexed with 0.
There are several ways to accomplish this -
PARTIAL VIEW: Since you are using Server Side data annotation as I see from the class definitions, then it is not a good idea to load dynamically with js. Because you will miss out all the validation that MVC 4 could have created automatically. So, the best solution I would suggest is taking the code that you are adding dynamically to a partial view file and then get the html with ajax call and then populating the HTML.
JS VALIDATION: But, if it is a must that you should use JS, then you have to add all the validation items yourself. To do that you have to do some extra works -
First, inspect the HTML with any developer tools, you will notice that there is a <span> attribute appended after each item to show the error which has a target mentioned. You have to append similar attributes to your elements
With MVC 4 unobtrusive validation, all the validation attributes and rules are added with the target element with data attributes. Each one is based one the validation they stands for. You have you create attributes similar to that.
Finally, after adding all the validation items in JS, reset the form so that it parses the new validations added and work accordingly. The code to parse the validations are here -
var form = $("form") //use more specific selector if you like
form.removeData("validator").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(form);
But I would prefer the partial view solution, since it will require least amount of re-work and also gives you option to keep all your validation in one place. You don't have to worry about new validations to be ported to js in future.