Blazor #page route url define with variable - asp.net-core

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";
}

Related

Use a route parameter in an attribute in a blazor server page

I am writing this page on a blazor-server application:
#page "/pageurl/{param:int}"
#attribute [AnAttribute(param)]
<div>#param</div>
#code
{
[Parameter]
public int param { get;set; }
}
As you can see, i am trying to pass the param as an argument of my attribute. It does not work. Maybe because when attribute is runned, blazor hasn't parsed parameters.
How can i use a page parameter as an attribute argument ?
Thanks
Parameters of an attribute must be known at compiletime. (Hard coded)
It makes no difference whether RazorPage or other classes.
The only way to work around this, could be Reflection.

How can I use "For" Keyword in IActionResult name of .net core?

As google suggest in this page:
https://support.google.com/webmasters/answer/7451184?hl=en
Simple URLs should convey content information
My product name is tools for car,so my url is http://localhost:4413/Product/tools-for-car
Now the problem is in the Controller,when I use this name in controller,Visual Studio report error for the keyword "For" as below:
I wonder if there is a way that I can use this url name?Or I can only change the name.Thank you.
What you probably want is to specify the routes explicitly:
[Route("[controller]")]
public class ProductController : Controller
{
[Route("tools-for-car")]
public IActionResult CarTools() { … }
}
See the Routing to controller actions documentation for more detail.

How are Razor Page View Component paths searched? I fail to get them working, .NET Core tries to include irrelevant model

I cannot get ViewComponent to be included in a page.
I have a folder structure:
- Pages
- Components
- ExternalSystems
- Default.cshtml
- Views
- Shared
- Components
- ExternalSystems
- Default.cshtml
Class file
public class Default : ViewComponent
{
private readonly Models.PermissionRegisterContext _context;
public Default(Models.PermissionRegisterContext context)
{
_context = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var externalSystems = await _context.ExternalSystem.ToListAsync();
return View("Default", externalSystems);
}
}
within Pages/Index.cshtml i'v tried to include this component by trial & error (i'm learning this stuff after all):
#await Component.InvokeAsync("ExternalSystems");
#await Component.InvokeAsync("/Pages/Components/ExternalSystems");
#await Component.InvokeAsync("/Pages/Components/ExternalSystems/Default");
#await Component.InvokeAsync("/Pages/Components/ExternalSystems/Default.cshtml");
#await Component.InvokeAsync("/Views/Shared/ExternalSystems");
I expected that exception will show places searched like it does for #Html.Partial:
InvalidOperationException: The partial view 'none' was not found. The following locations were searched: /Pages/none.cshtml /Views/Shared/none.cshtml
However for every single call to #await Component.InvokeAsync it spits this exception:
InvalidOperationException: Could not find an 'Invoke' or 'InvokeAsync' method for the view component '.Pages.Identities.IndexModel'.
First of all, it doesn't show me paths searched. Second, wait what? Why are you telling me something about Pages.Identities? Yes, I have that model, but it is nowhere referenced in neither Pages/Index.cshtml or View Component i'm trying to include.
Could someone please provide me with a guidance to determine: How view component paths are searched? For Razor Pages it is not documented, only in 3rd party site.
Anyway it doesn't work that way for me - what would be the debugging steps? Console debug doesn't show anything useful.
dotnet 2.0.7
Let's say you have created a view component as follows :
public class ExternalSystems : ViewComponent
{
public ExternalSystems()
{
//constructor can have dependencies injected.
}
public IViewComponentResult Invoke()
{
//View is a helper method available in classes inherited
//from ViewComponent that returns an instance of
//ViewViewComponentResult. It has multiple overloads as described
//later.
return View(viewName,viewModel);
}
}
View method has multiple overrides :
View() - use default view for view component without any viewmodel
View(viewModel) - use default view for view component with specified viewmodel
View(viewName) - use specified view for view component without any viewmodel
View(viewName,viewModel) - use specified view for view component with specified viewmodel
When you try to render this view component from a Controller, view will be looked up at following locations :
"/Views/{ControllerName}/Components/ExternalSystems/{ViewName}.cshtml" .
So, if you are using HomeController and have specified viewName as ExternalSystemsView in the View(viewName,viewModel) call , your path becomes
/Views/Home/Components/ExternalSystems/ExternalSystemsView.cshtml . This allows each controller to have its own custom view for the view returned by view component.
If the ExternalSystemsView.cshtml is not located at above path , it will be looked up at /Views/Shared/Components/ExternalSystems/ExternalSystemsView.cshtml
You can override the lookup position by passing the complete path of view - View("Views/Shared/Components/Common/YourView.cshtml") while calling View(viewName,viewModel) from your ViewComponent's Invoke method.
Note : If you don't specify a viewName, it defaults to Default.cshtml which is different from Index.html used for controllers
For your case, #await Component.InvokeAsync("ExternalSystems") is the correct call as it expects the viewcomponent name as parameter. ViewName will be picked up from what you have passed as the viewName parameter value to View(viewName,viewModel) call in your ViewComponent's Invoke method and will default to Default.cshtml if no viewname has been specified.
Alright, my mistake was that I didn't see I have decorated ...Pages.Identities.IndexModel with [ViewComponent]. It was unintentional and was just trying to make ViewComponents work by trial & error and failed to see this mistake.
Basically dotnet discovered type that didn't have either Invoke/InvokeAsync function and thus was stuck on that exception.
I validated that these paths are searched when providing "HelloWorld" as view name:
/Pages/Components/HelloWorld/HelloWorld.cshtml
/Views/Shared/Components/HelloWorld/HelloWorld.cshtml

MVC 4 is overwriting specific Action-Parameters

MVC 4 does present me some strange behaviour at the moment.
Imagine the following Code:
TestController.cs
public class TestController : Controller
{
public ActionResult Index(Function function, string action)
{
return View();
}
public class Function
{
public string Action { get; set; }
}
}
It seems, that when I call the URL directly through the browser (localhost:PORT/Test), the Action-Property gets automatically filled with "Index".
If the Action would be named "MySuperDuperActionWhichGetsInvokedQuiteOften", exactly this Methodname would be in the property.
Can somebody explain what MVC is doing here?
The Problem is, that I of course want to fill that stuff myself, for example through an AJAX-Query. But if MVC is filling in this property all by itself, this breaks some behaviour.
I could, of course, just rename my property and it would be working, but it would still be quite interesting what's going on.
EDIT
I would understand it that my second parameter, string action, get's filled with the method-name. But why on earth would MVC bind any property/parameter that is named the same to the request-value of it?
It is problem with default model binder. It "maps" request fields to properties in your class. There is an article of MSDN describing how does it works but to simply this situation the code will be like this:
Action = Request["action"] //where of course Request["action"] equals to name of your action

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