Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

URL Rewriting Module #16687

Open
wants to merge 34 commits into
base: main
Choose a base branch
from

Conversation

arkadiuszwojcik
Copy link
Contributor

@arkadiuszwojcik arkadiuszwojcik commented Sep 7, 2024

This PR partially addresses issue #9027. It introduces a new Rewrite module with, as of now, basic functionality that allows providing rewrite/redirect rules using Apache mod_rewrite syntax. This functionality is very basic but also very flexible, providing a good starting point for more features that might be requested in the future.

Few remaining questions are:

  1. Is proposed name good? Maybe it should be: OrchardCore.Rewrites or OrchardCore.Rewriter
  2. I am not sure about module priority, currently it is: InfrastructureService
  3. Looks like RewriteRules have some limitations regarding absolute addresses. I created issue related to that in aspnetcore project.

@arkadiuszwojcik arkadiuszwojcik marked this pull request as ready for review September 9, 2024 16:11
Copy link
Member

@MikeAlhayek MikeAlhayek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arkadiuszwojcik Thank you for this PR.

I am not a fan of the suggested UI. I think it would be much better and user friendly to have a UI similarity to the Tenants, Features, or Notifications UI where user can add a new rule "as a new entry" form.

Also add a way to edit one rule at a time. I also think it would be better to prompt the user with at the following fields:

  • Title
  • Description
  • Pattern
  • Replacement
  • SkipRemainingRules.

Or the following fields:

  • Title
  • Description
  • Pattern
  • SkipRemainingRules.

This way the user can provide meaning title/description for what the rules should actually do.

And even better, you can then easily implement sorting on the UI where one can drag/drop the entries which will change the order of the entries in the options.

Updated

I would rename the module to UrlRewriting

Also, how would this work with other tenants?

For example, if every tenant on my setup uses a subsdomain, can one tenant put a rule that would redirect all of the traffic to their instance instead?

.AddRedirect("^(?!www\\.).*", "https://site1.example.com", statusCode: StatusCodes.Status301MovedPermanently)

Or of every tenant is setup using a prefix, can I just write a rule that would redirect all other tenants to my instances?

.AddRedirect(@"^(.+)/$", "/site1/")

Please confirm that the two rules I shared above, are only applicable on one tenant only and will not impact other tenants.

@MikeAlhayek MikeAlhayek changed the title Rewrite module URL Rewriting Module Sep 9, 2024
@arkadiuszwojcik
Copy link
Contributor Author

@MikeAlhayek, thank you for your comments. Regarding the UI, I think the module should ultimately support both types of URL rewrites: UI-based and text-based. Text-based is more advanced because RewriteRule can be preceded by multiple RewriteCond, which allow for numerous checks based on variables like Cookie, DateTime, UserAgent, etc. These checks are complex to model in a graphical UI. I suggest having two tabs: one for "Simple Mode" with a user-friendly interface, and another for "Advanced Mode". In this PR I just added only advance one.

Regarding multitenancy, the URL rewrite middleware is registered separately for each tenant (each tenant has its own IApplicationBuilder instance), so when a request hits the rewrite middleware, it has already been routed to the proper tenant by OC. I tested a simple case with two tenants to verify that the rewrite rules from one do not affect the other. With this it is impossible to intercept and redirect traffic from other tenant.

@MikeAlhayek
Copy link
Member

@arkadiuszwojcik yes I understand. You can also model your UI just like Queries UI. This UI allows you to add different types of queries. In your case you can provide the advance rule, but will allow another person to also define a different rule.

Yea the renaming should work as long as the middleware is added after the tenant middle ware.

@arkadiuszwojcik
Copy link
Contributor Author

Regarding the module name, what do you think about 'UrlRewrites' instead of 'UrlRewriting'? To me, it seems much more aligned with the current module names, although I might be wrong.

@MikeAlhayek
Copy link
Member

Regarding the module name, what do you think about 'UrlRewrites' instead of 'UrlRewriting'? To me, it seems much more aligned with the current module names, although I might be wrong.

Personally, I would go with UrlRewriting.

@hishamco
Copy link
Member

Is there any reason not to use the URL Rewrite module available in ASP.NET Core? Regardless the issue that you mentioned

@arkadiuszwojcik
Copy link
Contributor Author

arkadiuszwojcik commented Sep 11, 2024

Is there any reason not to use the URL Rewrite module available in ASP.NET Core? Regardless the issue that you mentioned

Can you elaborate? This OC module is using ASP.NET Core Rewrite module internally, it just expose way to provide rewrite rules in OC admin menu.

Update:
@hishamco ASP.NET Rewrite middleware allows to define rewrite/redirect rules in few text formats: Apache and IIS, I chosen to use Apache as more popular.

@hishamco
Copy link
Member

Good, also I have another module on top of my head that might be related URL redirection

Arkadiusz Wójcik added 2 commits September 15, 2024 12:21
- Permissions refactor work
- Rename Permissions class to UrlRewritingPermissionProvider
Copy link
Contributor

This pull request has merge conflicts. Please resolve those before requesting a review.

@MikeAlhayek
Copy link
Member

@arkadiuszwojcik when you are done addressing my previous feedback, please request my review and I'll review this again.

image

@arkadiuszwojcik
Copy link
Contributor Author

@MikeAlhayek everything is fixed except suggested UI ... so will it be accepted as it is or should I mark is as draft and start working on more user frendly UI?

@MikeAlhayek
Copy link
Member

I would rather see the improved UI first. If you don't want to spend lots of time on it, I understand. I would at minimum like to see the UI layout similar to the Queries UI.

Once that is ready, please request my review and I'll test it out and provide you feedback.

@arkadiuszwojcik
Copy link
Contributor Author

arkadiuszwojcik commented Oct 2, 2024

@MikeAlhayek took me a while but I managed to crate first version of UI for this module. You can create first rule by editing some recipe by adding dependency to module:

"steps": [
  {
    "name": "feature",
    "enable": [
      ....
      "OrchardCore.UrlRewriting",
    ]
  },

and creation step:

{
  "name": "UrlRewriting",
  "Rules": [
    {
      "Name": "Img rewrite rule",
      "Pattern": "/img/(.*)",
      "Substitution": "/media/$1",
      "Flags": "L"
    }
  ]
}

This rule allow to access images by /img/ url instead of /media/

UrlRewriting


public sealed class AdminMenu : AdminNavigationProvider
{
public readonly IStringLocalizer S;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public readonly IStringLocalizer S;
internal readonly IStringLocalizer S;

builder
.Add(S["Configuration"], configuration => configuration
.Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting
.AddClass("urlrewriting").Id("urlrewriting")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.AddClass("urlrewriting").Id("urlrewriting")
.AddClass("urlrewriting")
.Id("urlrewriting")

return View(shape);
}

[HttpPost, ActionName(nameof(CreateRule))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[HttpPost, ActionName(nameof(CreateRule))]
[HttpPost]
[ActionName(nameof(CreateRule))]

return View(model);
}

[Admin("UrlRewriting/CreateRule", nameof(CreateRule))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[Admin("UrlRewriting/CreateRule", nameof(CreateRule))]
[Admin("UrlRewriting/CreateRule", "CreateRewritingRule")]

</div>
</form>

<script at="Foot" depends-on="jQuery">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you can, please don't use jQuery. we are trying to remove jQuery from all of our views

@MikeAlhayek
Copy link
Member

MikeAlhayek commented Oct 3, 2024

@arkadiuszwojcik I am going to push some changes to your branch. The display driver need a bit of work. I don't have time now to refactor it.

But here is a general feedback on what needs to be done there:

  1. on the index view, we don't render tables as you are doing. Instead, we render "Name". You can use shapes to render other info like pattern and action if needed under the name using shapes but not hard code them into the index view.
  2. The view model RewriteRuleViewModel should not need to have nested RewriteActionViewModel and RedirectActionViewModel you could replace these two by adding the following properties on the viewmodel directly
  • public bool AppendQueryString { get; set; } = true;
  • public RedirectType RedirectType { get; set; } = RedirectType.Found302;
  • public string Url { get; set; }
  • public bool SkipFurtherRules { get; set; }

this way you are not using key like RewriteAction.RewriteUrl, RedirectAction.RedirectUrl or RuleError

  1. In the RewriteRuleFiles.Edit.cshtml instead of having to write JS, you could use bootstap toggle instead.
  2. Not required for this PR, but I wish you used the same approach we use in the Queries UI so that the UI will be extensible instead. Because with that structure, you can then add "Rewrite" rule which will have a different (more simple) UI and another separate rule for Redirect with again a simple UI.

This is what we have in the queries UI. When you click "Add Query", you'll get a modal where you can select which query type to create. In your case it would be rule type. Then each query type would have a very simple UI for that rule time without the show/hide and all these properties.

image

@MikeAlhayek
Copy link
Member

@arkadiuszwojcik I am making changes to this PR. please don't make any changes. this will improve the PR

@MikeAlhayek
Copy link
Member

@arkadiuszwojcik I made lots of changes to your PR. The PR now uses the same UI as Queries so it is easily extensible.

To add new rules, all you need is to implement the following

services.AddRewriteRuleSource<UrlRewriteRuleSource>(UrlRewriteRuleSource.SourceName)
    .AddScoped<IDisplayDriver<RewriteRule>, UrlRewriteRuleDisplayDriver>();

Here is what you need to do

  1. Check the logic in Configure method in UrlRewriteRuleSource and UrlRedirectRuleSource
  2. Check the driver for each of the available rules UrlRewriteRuleDisplayDriver and UrlRedirectRuleDisplayDriver.
  3. It would be nice to add the rest of the events to IRewriteRuleHandler like Deleting, Deleted, Saving, Saved
  4. Test the rule to make sure that the configuration is doing the expected behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants