How can I prevent default notifications when encountering an error when using an external datasource as my datastore? - asp.net

I'm building a custom content part that fetches its information from an external repository, mostly following the advice found # How to change Orchard record repository and using a custom handler to fetch the data.
Working with external data stores opens up the possibility of all sorts of network exceptions, etc., which would cause the underlying record not be saved. However, if there's an exception thrown in the ContentHandler, it's swallowed up by the Invoke<TEvents> method so that (unless it's a "fatal" exception) the user wouldn't know about the exception and would be notified by the AdminController that "Your {0} has been saved.", when in fact it hasn't been.
A workaround that's obvious to me is to intercept the error somehow and notify the content driver for my content part, which exposes the executing AdminController. At that point, I can hook into the controller's ModelState and introduce an error, which would then be caught and then I'd be notified of an error without any false positive notifications.
Are there any other extensibility points available in Orchard that would handle this kind of external access better than altering the controller's ModelState via a content driver?

The easiest way would be to implement you own, simple, per-request storage object for those errors, ie.
public interface IErrorLog : IDependency {
public void Add(string message){ ... }
public IEnumerable<string> List() { ... }
}
public class DefaultErrorLog : IErrorLog { ... }
Inject IErrorLog in both your controller and handler. In the handler, catch all errors you need to catch and add them to the collection with Add(...). Then, in the controller, call List() and add model error for each entry.
UPDATE
If you're not in control of a Controller that updates your content item, then you should use a driver. Catch the exception during item save (the second Editor method with 3 params) and push some error info using AddModelError of the provided IUpdateModel object. Way easier, but you'll be able to catch only those errors that happen when an item is saved.
UPDATE 2
If you're not in control of a Controller that updates your content item but would like to have a pure solution and gain control over the whole process, you can use your own controller for editing items. In order to do that:
create a new controller (or copy and alter the default AdminController found in Orchard.Core\Contents) first
now tell Orchard to use this controller instead of the default one for all types that contain your custom part. It can be done by putting something like this in your handler:
OnGetContentItemMetadata<MyCustomPart>((context, part) =>
{
context.Metadata.AdminRouteValues = new RouteValueDictionary
{
{ "Area", "My.Module" },
{ "Controller", "Admin" },
{ "Action", "Edit" },
{ "id", context.ContentItem.Id }
};
});

Related

How to set validation state in a custom validation handler in a Blazor EditForm

I am struggling with a custom validation in an EditForm using basic validation in my Blazor server-side app. In this MS doc it is stated
A handler for the OnValidationRequested event of the EditContext
executes custom validation logic. The handler's result updates the
ValidationMessageStore instance.
but nothing is mentioned on how to inform the validation logic if the custom validation was successful or failed. The code below shows how error messages can be added in a custom validation handler, but OnValidSubmit is called during the await-ing of the DB query in the custom validation handler. So it seems necessary, to perform the custom validation also in OnValidSubmit, which is acceptable in my case but not nice.
Q: Is there no other/nicer way to inform the validation logic about the result of a custom validation in order to prevent having to re-check inside OnValidSubmit?
Here is my code in OnParametersSetAsync:
// Create EditContext
editContext = new EditContext(assignment);
// Create additional message store for the custom validation messages
validationMessageStore = new(editContext);
// Add additional validation handler
editContext.OnValidationRequested += OnValidationRequestedAsync;
Here is the code for the custom validation handler:
private async void OnValidationRequestedAsync(object sender, ValidationRequestedEventArgs e)
{
// clear previous error messages
validationMessageStore.Clear();
// check DB if Title already exists
bool exists = await myService.IsPresent(myModel.Title);
// While waiting for this async process, OnValidSubmit gets called
if (exists)
{
// yes, so add a validation message
validationMessageStore.Add(() => myModel.Title, "The Title is already used.");
// inform ValidationSummary that a new error message has been added
editContext.NotifyValidationStateChanged();
}
}
In OnValidSubmit I have to re-check:
// check DB if Title already exists
bool exists = await myService.IsPresent(myModel.Title);
if (exists)
{
// do nothing if invalid (error message is displayed by OnValidationRequestedAsync)
return;
}
// ...
Have you looked at Blazored.Validation? It takes all the pain out of this. You create a validator (or you can use attributes, but for custom validation, I find the validator approach much easier), then simply add the <FluentValidationValidator /> tag inside your form, and it all just works. Validation is fired as appropriate, messages are shown as expected and you OnValidSubmit just doesn't get called if there are validation errors.
Hope that helps.
I found a possible solution (at least in general) by decorating the property with a custom validation attribute. Just add
[CustomValidation(typeof(myModelClass), "OnValidateTitle")]
public string Title{ get; set; }
to the property of the model class and add the static method in the model class that performs the custom validation and returns an appropriate status
public static ValidationResult OnValidateTitle(string title)
{
if (...)
{
return new ValidationResult("Title is ...");
}
else
{
return ValidationResult.Success;
}
}
While this approach should work in general, in my particular case it didn't work, because I do not have access to the database context from the static method.

Modifying swagger annotations at runtime

I have a Swagger API documentation. Some of the methods I want to hide dynamically, depending on user permissions for reading/ accessing the API.
Since I couldn't come up with a Swagger functionality on this, I have implemented a ContainerRequestFilter on my service, which is executed when getting the openapi.json document for the Swagger UI. This filter is scanning all methods containing the #Operation annotation and changing the field hidden from true to false for users with the correct permission.
import io.swagger.v3.oas.annotations.Operation;
#Provider
#Priority(Priorities.USER)
public class MyFilter implements ContainerRequestFilter{
public void filter(ContainerRequestContext crc) throws IOException{
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage("my.package"))
.setScanners(new MethodAnnotationsScanner()));
// get all Operation methods
Set<Method> resources = reflections.getMethodsAnnotatedWith(Operation.class);
for(Method method : resources) {
method.setAccessible(true);
Operation operation = method.getAnnotation(Operation.class);
InvocationHandler h = Proxy.getInvocationHandler(operation);
Field hField = h.getClass().getDeclaredField("memberValues");
hField.setAccessible(true);
Map memberValues = (Map) hField.get(h);
for(Object key : memberValues.keySet()) {
System.out.println("memberValue: "+key+", "+memberValues.get(key));
}
memberValues.put("hidden", false);
System.out.println("operation is hidden?: " +operation.hidden());
}
}
}
The code seems to work insofar as the hidden field value is changed from true to false inside this filter class. However, the openapi.json document returned from the same service request does not reflect those changes.
Why are the changes on the modified annotations not consistent within the executed request?
This approach does not seem to be viable. Reflection changes the annotations globally, so other requests would read the same changes annotations which is not what I wanted here.
I ended up going with the filtering of annoations as described here.

PRG Pattern in ASP.Net MVC?

I'm new to ASP.Net MVC. In PHP, I always use the PRG pattern even when the post request was invalid. It was pretty easy with session flashes (also user friendly).
In ASP.Net MVC, however, I don't see an easy way to do PRG when the request is invalid. I could think of some ways, but I don't think they are good practices and put some extra unnecessary work.
Moreover, from a couple of articles that I've read, a PRG when the request was invalid was discouraged. If it's a bad practice, then what's the better way to handle unsuccessful post requests? Is it really better off without the PRG? And should I just let the rather annoying browser warnings when a user tries to refresh the page?
In Mvc, it's normal practice to handle your Post Actions as it follows:
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult LoginForm(LoginViewModel loginViewModel)
{
if (!ModelState.IsValid)
return View("Login", loginViewModel);
return Redirect("/");
}
As you can see, the property ModelState.IsValid will tell you if the request is invalid, therefore giving you the ability to return the same view and display the error messages in the ValidationSummary when the Post request contains an error. This is the code for the View:
#using (Html.BeginForm("LoginForm", "Account"}))
{
#Html.ValidationSummary() // THIS WILL SHOW THE ERROR MESSAGES
#Html.AntiForgeryToken()
#Html.TextBoxFor(x => x.Email)
#Html.PasswordFor(x => x.Password)
<button type="submit">Submit</button>
}
We have been using PRG pattern in our asp.net mvc web apps for about 5 years. The main reason we adopted PRG was to support browser navigation (eg back, forward). Our web apps are used by customer and for front/back office operations. Our typical web page flow is starts with a login, then progresses via many list/detail view. We also incorporate partial views which also have their own viewmodel. List views will have links (GETS) for navigation. Detail views will have forms (POSTS) for navigation.
Keys aspects of our PRG:
We incorporate viewmodels so each view has a viewmodel (all data access is done in the viewmodel).
Each viewmodel has a set() & get() method to maintain the key data field values associated with the most recent instance of the view. The set/get values are persisted in sessionstate.
The set method has a parameter for each value that needs to be set. The get method is just called from the viewmodel constructor to populate the viewmodel's public "key" values.
The viewmodel will also have a public load() method that get all neccessary data for its view.
Our PRG pattern overview:
In controllers we have a separate GET method and a POST method for each action. The GET only displays a view; the POST processes the posted data.
For list (menu) views, the controller GET method calls the target view's set('item key values here') method, then invokes a RedirectToAction to to the target view's controller GET action.
The controller GET method will instantiate the viewmodel (thus causing get of set values), call its load method which uses the set/get key values to get it data, and returns the view/viewmodel.
The controller POST method will either have the viewmodel save the valid posted data then redirect to the next desired page (probably the previous list menu) -OR- if redisplay the current view if the data is invalid.
I have not documented all the PRG flow senarios that we implemented, but the above is the basic flow.
SAMPLE VIEWMODEL SET/GET METHODS
private void GetKeys() {
Hashtable viewModelKeys;
if (SdsuSessionState.Exists("RosterDetail"))
{
viewModelKeys = (Hashtable)SdsuSessionState.Get("RosterDetail");
EventId = (int)viewModelKeys["EventId"];
SessionNo = (int)viewModelKeys["SessionNo"];
viewModelKeys = null;
}
}
public static void SetKeys(int eventId, int sessionNo) {
Hashtable viewModelKeys = new Hashtable();
viewModelKeys.Add("EventId",eventId);
viewModelKeys.Add("SessionNo",sessionNo);
SdsuSessionState.Set("RosterDetail",viewModelKeys);
viewModelKeys = null;
}
SAMPLE CONTROLLER
[AcceptVerbs("Get")]
public ActionResult MenuLink(int eventId, int sessionNo, string submitButton) {
if (submitButton == RosterMenu.Button.PrintPreview) {
// P-R-G: set called viewmodel keys.
RosterDetail.SetKeys(eventId,sessionNo);
// Display page.
return RedirectToAction("Detail","Roster");
}
if (submitButton == RosterMenu.Button.Export) { etc ...}
}

How can Tridion 2011 Event System prevent a single page from publishing?

Event System handler code:
[TcmExtension("My Handler")]
public sealed class EventSystem : TcmExtension
{
public EventSystem()
{
EventSystem.Subscribe<Page, PublishEventArgs>((page, e, phases) => {
if (shouldTerminatePublishing(page))
{
throw new Exception(ex, page);
}
}, EventPhases.Initiated, EventSubscriptionOrder.Normal);
}
}
With the code above when multiple pages are being published and Event System is only about to block one of them (by throwing an exception), then all pages are effectively prevented from being published too. "Ignore Failures While Generating Publishable Content" check box does not affect this behavior.
How to prevent any given page from publishing but still allow all the rest to be published?
EDIT
Updated code as per Quirijn's suggestion:
public class MyResolver: IResolver
{
public void Resolve(
IdentifiableObject item,
ResolveInstruction instruction,
PublishContext context,
ISet<ResolvedItem> resolvedItems)
{
var page = item as Page;
if (null != page && instruction.Purpose == ResolvePurpose.Publish)
{
try
{
// Evaluate whether publishing is allowed
}
catch (Exception ex)
{
resolvedItems.Clear();
}
}
}
}
Some objections (or rather follow-up questions) so far:
There's no sensible way to provide explicit feedback to the user when item gets excluded (except advising to check "Show Items to Publish" option), is there?
Custom resolver must explicitly account for all items types, that is: not only for 'Page' but also 'StructureGroup' and 'Publication', must it not?
Given that evaluation code might be expensive (web service call), is there any way to cache it's result at least between preparing "Show Items to Publish" list and performing actual publishing? (In such case evaluation occurs at least twice).
EDIT 2
After looking into standard resolvers' implementation:
Is it necessary/preferably to implement IBulkResolver as well?
You shouldn't do this in the event system but in a custom resolver. This is a piece of code which gets executed to determine which pages / components should be effectively published when an item is put in the publish queue.
Here you can filter out any page or component which you do not want to be published.
See How to remove items from publishing using a Tridion Resolver?.

Access/use the same object during a request - asp.net

i have a HttpModule that creates an CommunityPrincipal (implements IPrincipal interface) object on every request. I want to somehow store the object for every request soo i can get it whenever i need it without having to do a cast or create it again.
Basically i want to mimic the way the FormsAuthenticationModule works.
It assigns the HttpContext.User property an object which implements the IPrincipal interface, on every request.
I somehow want to be able to call etc. HttpContext.MySpecialUser (or MySpecialContext.MySpecialUser - could create static class) which will return my object (the specific type).
I could use a extension method but i dont know how to store the object so it can be accessed during the request.
How can this be achieved ?
Please notice i want to store it as the specific type (CommunityPrincipal - not just as an object).
It should of course only be available for the current request being processed and not shared with all other threads/requests.
Right now i assign my CommunityPrincipal object to the HttpContext.User in the HttpModule, but it requires me to do a cast everytime i need to use properties on the CommunityPrincipal object which isnt defined in the IPrincipal interface.
I'd recommend you stay away from coupling your data to the thread itself. You have no control over how asp.net uses threads now or in the future.
The data is very much tied to the request context so it should be defined, live, and die along with the context. That is just the right place to put it, and instantiating the object in an HttpModule is also appropriate.
The cast really shouldn't be much of a problem, but if you want to get away from that I'd highly recommend an extension method for HttpContext for this... this is exactly the kind of situation that extension methods are designed to handle.
Here is how I'd implement it:
Create a static class to put the extension method:
public static class ContextExtensions
{
public static CommunityPrinciple GetCommunityPrinciple(this HttpContext context)
{
if(HttpContext.Current.Items["CommunityPrinciple"] != null)
{
return HttpContext.Current.Items["CommunityPrinciple"] as CommunityPrinciple;
}
}
}
In your HttpModule just put the principal into the context items collection like:
HttpContext.Current.Items.Add("CommunityPrincipal", MyCommunityPrincipal);
This keeps the regular context's user property in the natural state so that 3rd party code, framework code, and anything else you write isn't at risk from you having tampered with the normal IPrincipal stroed there. The instance exists only during the user's request for which it is valid. And best of all, the method is available to code as if it were just any regular HttpContext member.... and no cast needed.
Assigning your custom principal to Context.User is correct. Hopefully you're doing it in Application_AuthenticateRequest.
Coming to your question, do you only access the user object from ASPX pages? If so you could implement a custom base page that contains the cast for you.
public class CommunityBasePage : Page
{
new CommunityPrincipal User
{
get { return base.User as CommunityPrincipal; }
}
}
Then make your pages inherit from CommunityBasePage and you'll be able to get to all your properties from this.User.
Since you already storing the object in the HttpContext.User property all you really need to acheive you goal is a Static method that acheives your goal:-
public static class MySpecialContext
{
public static CommunityPrinciple Community
{
get
{
return (CommunityPrinciple)HttpContext.Current.User;
}
}
}
Now you can get the CommunityPrinciple as:-
var x = MySpecialContext.Community;
However it seems a lot of effort to got to avoid:-
var x = (CommunityPrinciple)Context.User;
An alternative would be an Extension method on HttpContext:-
public static class HttpContextExtensions
{
public static CommunityPrinciple GetCommunity(this HttpContext o)
{
return (CommunityPrinciple)o.User;
}
}
The use it:-
var x = Context.GetCommunity();
That's quite tidy but will require you to remember to include the namespace where the extensions class is defined in the using list in each file the needs it.
Edit:
Lets assume for the moment that you have some really good reason why even a cast performed inside called code as above is still unacceptable (BTW, I'd be really interested to understand what circumstance leads you to this conclusion).
Yet another alternative is a ThreadStatic field:-
public class MyModule : IHttpModule
{
[ThreadStatic]
private static CommunityPrinciple _threadCommunity;
public static CommunityPrinciple Community
{
get
{
return _threadCommunity;
}
}
// Place here your original module code but instead of (or as well as) assigning
// the Context.User store in _threadCommunity.
// Also at the appropriate point in the request lifecyle null the _threadCommunity
}
A field decorated with [ThreadStatic] will have one instance of storage per thread. Hence multiple threads can modify and read _threadCommunity but each will operate on their specific instance of the field.

Resources