I am working on a mvc core 3.1 application. Seo requirements are to show product name with main site instead of complete url.
My original url is
www.abc.com/Fashion/ProductDetail?productId=5088&productName=AliviaBlack&brandId=3
Requirement are
www.abc.com/alivia-black
I have tried following by using attribute routing
endpoints.MapControllerRoute(
name: "SpecificRoute",
pattern: "/{productName}",
defaults: new { controller = "Fashion", action = "ProductDetail", id= "{productId}" });
In view page
<a asp-route-productId="@product.ProductId"
asp-route-productName="@Common.FilterURL(product.ProductName)"
asp-route-brandId="@product.FashionBrand.FashionBrandId" asp-route="SpecificRoute">
Result is
www.abc.com/alivia-black?productId=5223&brandId=4
How to remove question mark and parameters after it.
CodePudding user response:
First off, URIs need to be resilient. You say your current requirement is to have URIs like this:
www.abc.com/alivia-black
i.e.:
{host}/{productName}
That's a very bad URI template because:
- It does not uniquely identify the product (as you could have multiple products with the same name).
- It will break existing links from external websites if you ever rename a product or replace a product with the same name. And this happens a lot in any product database.
- Because you're putting the
{productName}
in the "root" of your URI structure it means it's much harder to handle anything else besides viewing products (e.g. how would you have a/contact-us
page? What if you had a product that was namedcontact-us
?)
I stress that is is very important to include an immutable key to the entity being requested (in this case, your productId
value) in the URI and use that as a primary-reference, so the productName
can be ignored when handling an incoming HTTP request. This is how StackOverflow's and Amazon's URIs work (you can trim off the text after a StackOverflow's question-id and it will still work: e.g.
https://stackoverflow.com/questions/69748993/how-to-show-seo-friendly-url-in-mvc-core-3-1
https://stackoverflow.com/questions/69748993
I strongly recommend you read this article on the W3.org's website all about designing good URIs, as well as other guidance from that group's homepage.
I suggest you use a much better URI template, such as this one:
{host}/products/{productId}/{productName}
Which will give you a URI like this:
abc.com/products/5088/alivablack
Handling such a link in ASP.NET MVC (and ASP.NET Core) is trivial, just set the route-template on your controller action:
[Route( "/products/{productId:int}/{productName?}" )]
public async Task<IActionResult> ShowProduct( Int32 productId, String? productName = null )
{
// Use `productId` to lookup the product.
// Disregard `productName` unless you want to use it as a fallback to search your database if `productId` doesn't work.
}
As for generating URIs, as I recommend against using TagHelpers (e.g. <a asp-route-
) because they don't give you sufficient control over how the URI is rendered, instead you can define a UrlHelper
extension method (ensure you @import
the namespace into your .cshtml
pages (or add it to ViewStart
):
public static class MyUrls
{
public static String ShowProduct( this IUrlHelper u, Int32 productId, String productName )
{
const String ACTION_NAME = nameof(ProductsController.ShowProduct);
const String CONTROLLER_NAME = "Products"; // Unfortunately we can't use `nameof` here due to the "Controller" suffix in the type-name.
String url = u.Action( action: ACTION_NAME, controller: CONTROLLER_NAME, values: new { productId = productId, productName = productName } );
return url;
}
}
Then you can use it like so:
<a href="@Urls.ShowProduct( 5088, "aliviablack" )">View AliviaBlack</a>
You can also make ShowProduct
accept one of your Product
objects directly and then pass the values on to the other overload (defined above) which accepts scalars:
public static String ShowProduct( this IUrlHelper u, Product product )
{
String url = ShowProduct( u, productId: product.ProductId, productName: product.ProductName );
return url;
}
Then you can use it like so (assuming product
is in-scope):
<a href="@Urls.ShowProduct( product )">@product.ProductName</a>