Code documentation comments for ASP.NET razor pages? - asp.net-core

Is it possible to add code documentation comments to razor pages (resp.: components)?
I found it possible to use standard documentation comments for page parameters (which are declared in #code blocks), but I am still looking for a way to add comments for the component (class) itself.
For parameters, this works:
#code
{
/// <summary> The Id of the selected account. </summary>
[Parameter]
public int SelectedAccount { get; set; }
}
So, for a class, it should be something like this:
#***
<summary> This component renders a table. </summary>
***#
or
#classcomment{
<summary> This component renders a table. </summary>
}
Does anyone know a way?

The simple answer is "it cannot be done", for now.
I've opened a feature request on the razor repo. If you want this feature, please upvote it!
Until this is possible, the accepted answer is a very nice workaround (but requires an extra file, which is a nuisance).

I don't think this is possible just from .razor files. However, you can add a "code-behind" file where you could place the documentation (this is not ideal, of course, because the documentation is separate from the Razor code).
For example, let this be your Component.razor:
<p>Some component</p>
#code
{
/// <summary> The Id of the selected account. </summary>
[Parameter]
public int SelectedAccount { get; set; }
}
You can create Component.razor.cs with the component's documentation comment (the filename is not important, but it's a nice convention to follow this scheme):
/// <summary> This component renders a table. </summary>
partial class Component { }
Note that the namespaces must match (in this example I haven't used any, but you could specify namespace via #namespace directive in the .razor file or folder structure, and namespace block in the .cs file as usual).

Related

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

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.

Blazor #page route url define with variable

I have a question for Blazor Server Side.
I want to #page route url define with variable or property.
I can use now with below default method
#page "/route-url"
<h1>Page Test</h1>
#code {
}
But i want use like as below method
#page MenuItem.Role
<h1>Page Test</h1>
#code {
}
I'm tried above method then throwed exception. Like as below exception.
C:\Projects\TestBlazorProject\Pages\TestPage.razor(1,7): error RZ1016: The 'page' directive expects a string surrounded by double quotes. [C:\Projects\TestBlazorProject\TestBlazorProject.csproj]
How to define #page route url with any different variable or any class property?
Can this be done?
Yes
How?
Page file
#attribute [Route(PageRoute.TaskList)]
<div>PAGE HTML HERE</div>
#code{ ... }
PageRoute.cs:
public static class PageRoute
{
public const string TaskList = "/route-url";
}
Explanation
The page directive gets compiled down to an attribute and has the same restrictions as C# attributes.
You can use the #attribute with the [Route] attribute and use string concatenation instead of string interpolation to define a constant for the route, since that's what the C# compiler supports.
Why would anybody do this?
This is a good coding practice, because you are not hardcoding the page/component name in multiple places, but only in one place.
So one fine day when you manager asks to change page name "Earth" to "Planet3",
you just change it in 1 place, and be 98% sure that your app wont crash because of it.
#page isn't C#, it's Razor talk. Razor files are pre-compiled into c# files during compilation.
As an example, this is the important section of the C# pre-compiled file for Index.razor (Index.razor.g.cs):
[Microsoft.AspNetCore.Components.RouteAttribute("/")]
public partial class Index : Microsoft.AspNetCore.Components.ComponentBase
{
#pragma warning disable 1998
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
__builder.AddMarkupContent(0, "<h1>Hello, world!</h1>\r\n\r\nWelcome to your new app.\r\n\r\n");
__builder.OpenComponent<Blazor.Starter.Shared.SurveyPrompt>(1);
__builder.AddAttribute(2, "Title", "How is Blazor working for you?");
__builder.CloseComponent();
}
#pragma warning restore 1998
}
Note that #page has become a compile time attribute [Microsoft.AspNetCore.Components.RouteAttribute("/")]. It's fixed at compiletime, you can't change it at runtime.
Routes are set this way because the router builds a routemap - essentially a dictionary of route url/component class pairs - when the application loads by trawling the application assembly for any component classes with a Route attribute. On a routing event it reads the new url, finds the component class and loads it into the layout. Any variables - stuff in curly brackets - get passed into the component as Parameters.
You haven't made it clear what the line below is supposed to do:
#page MenuItem.Role
Do you want to capture a variable supplied in the route into MenuItem.Role?
Do you want to set this page's route to the value in MenuItem.Role?
If 1, either the other answers will work for you. If 2, you'll need to consider writing your own router. A subject beyond a simple answer here.
I think you can achieve that by following.
#page "/{Role}"
#code{
[Parameter]
public string Role { get; set; }
}
Building off of the above you can I was able to get this to work with the code isolation approach.
Client/Pages/Foo/
----Index.razor
----Index.cs
namespace Client.Pages.Foo;
[Microsoft.AspNetCore.Components.RouteAttribute(Path)]
public partial class Index
{
public const string Path = "/Foo";
}

User customizable validation with metadata saved in a database

I'm working on an application which should validate the model based on some metadata saved in a database. The purpose of this is to allow administrators change how some models are validated, without changing the code, depending on clients' preferences. The changes are applied for the entire application, not for specific users accessing it. How it is changed, doesn't matter at the moment. They could be modified directly on the database, or using an application. The idea is that they should be customizable.
Let's say i have the model "Person" with the property "Name" of type "string".
public class Person
{
public string Name { get; set; }
}
This model is used by my app which is distributed and istalled on several servers. Each of them is independent. Some users may want the Name to have maximum 30 letters and to be required when creating a new "Person", others may want it to have 25 and not to be required. Normally, this would be solved using data annotations, but those are evaluated during the compile time and are somehow "hardcoded".
Shortly, I want to find a way to customize and store in a database how the model validates, without the need of altering the application code.
Also, it would be nice to work with jquery validation and have as few request to database(/service) as possible. Besides that, i can't use any known ORM like EF.
You could create a custom validation attribute that validates by examining the metadata stored in the database. Custom validation attributes are easy to create, simply extend System.ComponentModel.DataAnnotations.ValidationAttribute and override the IsValid() method.
To get the client side rules that work with jQuery validation you will need to create a custom adapter for the type of your custom validation attribute that extends System.Web.Mvc.DataAnnotationsModelValidator<YourCustomValidationAttribute>. This class then needs to be registered in the OnApplicationStart() method of your Global.asax.
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(YourCustomValidationAttribute), typeof(YourCustomAdapter));
Here's an example adapter:
public class FooAdapter : DataAnnotationsModelValidator<FooAttribute>
{
/// <summary>
/// This constructor is used by the MVC framework to retrieve the client validation rules for the attribute
/// type associated with this adapter.
/// </summary>
/// <param name="metadata">Information about the type being validated.</param>
/// <param name="context">The ControllerContext for the controller handling the request.</param>
/// <param name="attribute">The attribute associated with this adapter.</param>
public FooAdapter(ModelMetadata metadata, ControllerContext context, FooAttribute attribute)
: base(metadata, context, attribute)
{
_metadata = metadata;
}
/// <summary>
/// Overrides the definition in System.Web.Mvc.ModelValidator to provide the client validation rules specific
/// to this type.
/// </summary>
/// <returns>The set of rules that will be used for client side validation.</returns>
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
return new[] { new ModelClientValidationRequiredRule(
String.Format("The {0} field is invalid.", _metadata.DisplayName ?? _metadata.PropertyName)) };
}
/// <summary>
/// The metadata associated with the property tagged by the validation attribute.
/// </summary>
private ModelMetadata _metadata;
}
This may also be useful if you would like to asynchronously call server side validation http://msdn.microsoft.com/en-us/library/system.web.mvc.remoteattribute(v=vs.108).aspx

How to allow a user to enter html comments

Using MVC, EF 4.2. I am working on an application that has a comment section. Right now if a user enters a comment that contains HTML e.g.
<b>text</b>
and hits submit i get the message
"A ptentially dangerous Request.Form value was detected..."
How do i handle html on the way into the db? Should I just strip the html? Or encode it? I tried server.htmlencode the text but i still had the same error message.
I have read a number of posts on the matter including some here at SO - this one and this one
Ideally, i'd like to be able to allow a limited number of html tags such as em strong, a. Would Anti-XSS, HTML Agility, some kind of BB code, or a markdown style editor still be the recommended way? I know Jeff has a whitelist bit of code - however it is few yrs old.
you can do
[ValidateInput(false)]
public ActionResult foo()
{
}
or you can decorate the model property with AllowHtml
public class Foo
{
[AllowHtml]
public string bar{ get; set; }
}
You may also need to set the requestValidationMode in your web.config:
</system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
See this link for more details.
MVC has an attribute that allows you to specify a property should allow html without disabling validation completely. It's still dangerous, but it can be limited to a single property so the risk can be mitigated. Here is the MSDN article for the AllowHtmlAttribute. Proper usage of the attribute should be to decorate the appropriate property in your model:
public class MyModel
{
public MyModel()
{
}
// Some other stuff in here
[AllowHtml]
[HttpPost]
public string MyHtmlString { get; set; }
}
My solution for allow html incomments is as follows:
AllowHtml on the CommentText property in my comment class
Allow a narrow subset of tags. Use an Html Sanitizer class to scrub Html and inline script that is not allowed via a whitelist
Then save the result to the db as i normally would
At output time, use Html.Raw to show the Html in the comments

How to get full intellisense tooltip comments working?

I've got some C++/CLI software which is all nice and documented in a C#'ish kind of way which means DOxygen is able to pull it out into some nice html. Is there any way I can get that same information to appear in the intellisense tool tips the way that the .net framework does?
For example, lets say this is my header file (MyApp.h):
/*************** MyApp.h ***************/
/// My namespace containing all my funky classes
namespace MyNamespace
{
using namespace System;
ref class WorldHunger;
/// A truly elegent class which solves all the worlds problems
public ref class MyClass
{
public:
/// Constructs a MyClass
MyClass()
{
}
/// <summary>Attempts to fix world hunger</summary>
/// <param name="problem">The problem to try and fix</param>
/// <returns>Whether or not the problem was solved</param>
bool FixWorldHunger( WorldHunger^ problem );
};
}
...and this it's corresponding implementation:
/*************** MyApp.cpp ***************/
#include "MyApp.h"
using namespace MyNamespace;
MyClass::MyClass()
{
}
bool MyClass::FixWorldHunger( WorldHunger^ problem )
{
bool result = false;
/// TODO: implement something clever
return result;
}
Here's what intellisense does for built in functions when I'm typing:
http://www.geekops.co.uk/photos/0000-00-02%20%28Forum%20images%29/BrokenIntellisense1.jpg
Here's what intellisense does for my own functions when I type:
http://www.geekops.co.uk/photos/0000-00-02%20%28Forum%20images%29/BrokenIntellisense2.jpg
Surely there's a way to do this?
Just to summarise, for this to work you need your comments in a compatible form:
/// <summary>
/// Retrieves the widget at the specified index
/// </summary>
/// <param name="widgetIndex">Index of the widget to retrieve.</param>
/// <returns>The widget at the specified index</returns>
Widget* GetWidget(int widgetIndex);
Then you simply right-click on the project in Visual Studio and go to properties > configuration properties > C/C++ > Output Files and change Generate XML Documentation Files to Yes.
When you rebuild your project ad import it somewhere else, you should see fully documented tooltips appear.