I've read through a number of SO questions on this topic, but all seem to be dealing with where you should put this call.
My problem is different: I have already done the WebSecurity.InitializeDatabaseConnection() call, and have set breakpoints so I know it's been executed. But I still get the invalid operation exception saying I have to call it.
Unlike most of the other questions which are encoutering it in an MVC controller action, I'm encountering it an HttpModule I have written to accomplish Authentication for a REST WebAPI controller. The Init call contains the WebSecurity.InitializeDatabaseConnection call. The OnAuthenticationRequest method then extracts username and password information from the request's Authorization header, and calls the ValidateUser method of the SimpleMembershipProvider. This is where I get the exception
You must call the WebSecurity.InitializeDatabaseConnection method
before you call any other method of theWebSecurityclass.
So
a) why am I getting this exception when I have already fulfilled the conditions not to get it.
b) what can be done about it?
After hours of facing the same problem and setting many many breakpoints I found a solution for my environment, I hope others can benefit from it:
inside Folder App_Start
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var attr=new InitializeSimpleMembershipAttribute();
// here is the important part
attr.OnActionExecuting(new ActionExecutingContext());
filters.Add(attr);
}
}
Background: In my app I have 2 views:
/Account/Register: this is part of the standard asp.mvc 4 templates and has the attribute [AllowAnonymous]
/Task: this is the "Index"-View of a todo-list, The TaskController has the attribute [Authorize(Roles="Administrator")]
I always had the problem in the following sitation:
debugging session in browser
successfull login
open the view "/Task" which requires authorization
switch to visual studio and keep the browser open
stop debugging
change some code inside a controller (setting some whitespace is enough)
start debugging session
press refresh in browser
At this point the method "OnActionExecuting" inside the class InitializeSimpleMembershipAttribute was not yet called (determined by setting breakpoints)!
But when I opened a View which doesn't require Authorization (i.e. /Account/Register) the Method "OnActionExecuting" was called. After this I could call /Task without errors.
So the solution was to make a "pseudocall" for "OnActionExecuting" on application-startup.
You should not need to init the database connection every time your HttpModule is called. Just do it once at application start up. Here is an article that explains how to use SimpleMembership for authentication/authorization of Web API calls. There is also a link to source code for the example project.
Related
I just found out that "RequestFeatures" is a thing in ASP.net Core, news to me..
Anyways..
I have created a middleware that needs to alter the response body and the headers provided with the response.
Im used to do this by alterntiv it directly on the HttpContext context-object provided as an argument to the Invoke-method.. however I read somewhere that context.Features is the way to go due to optimizations and what not.. (is this true?, I get that its alot easiter to test the Features, than to "mock" an entire HttpContext which has been historically a painful thing to do..) So I created my own implementation of the HttpResponseFeature and registred it in my Invoke method using:
httpContext.Features.Set<IHttpResponseFeature>
(
//Registering my own HttpResponseFeature that takes an argument..
new MyHttpResponseFeature(httpResponseMessage)
);
However, the OnCompleted or the OnStarting-methods never runs. I have added a few breakpoints to validate this, but the breakpoints are never hit. Am I missing something?
Turns out that the FeatureCollection/RequestFeatures is a "new" thing if your building your very own custom HTTP server ontop of ASP.net Core.
An article that covers this fairly well is this one:
https://reynders.co/use-iserver-from-aspnet-core-to-create-your-own-web-server/
Modifying Piranha for 'headless' scenario: I separated the api into its own REST API, and the MVC into its own UI that calls the REST API. Mostly it works well, but now an obstacle. My REST API serializes the result from the Piranha api, and my MVC web deserializes it into a Piranha StandardPage type. This works for all fields except Media, which is always null. Using newtonsoft.Json.
The Media property is defined in Piranha.Extend.Fields.MediaFieldBase with internal set, which explains why I can't deserialize into it. So I added a [JsonProperty] attribute to the Media property. Once I did this, then the Media field was correctly deserialized by the MVC, and images appeared.
But then I found this broke something else: in the manager, when I try to save a page with a Hero image, the Save buttons spins, stops as though it succeeded, but the toast never appears saying success. I set Debug logging for Microsoft.AspNetCore.Mvc.Infrastructure, and see there's a model state error when this happens:
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed controller factory for controller Piranha.JA.Manager.Controllers.PageApiController (Piranha.JA.Manager)
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter[1]
The request has model state errors, returning an error response.
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
Request was short circuited at action filter 'Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter'.
I can debug into the PageApiController for most operations, but when Save executes none of the breakpoints are hit.
I also tried just removing the internal accessor from set (and removing [JsonProperty]) and got the same behavior: can deserialize Media ok but trying to save page with a Hero from manager gives model state error.
Would love to write up some documentation on how to go headless if we can solve this.
We've been debugging this one for a few days, can anyone help?
In the branch I pushed I actually changed the accessor to public, so you shouldn’t need the additional attribute. This is also merged to master and will be released in 8.3.
Regards
I posted this on the Piranha CMS github and one of its authors, Hakan Edling, answered in just a few hours:
"The root issue is that when a new item is selected in the manager in the media picker, the media model that is assigned contains a formatted string size "xxx kb". When ASP.NET tries to deserialize this into a long it fails.
So changing the following line in the .vue components for the media-based fields:
this.model.media = media;
to
this.model.media = {
id: media.id,
folderId: media.folderId,
type: media.type,
filename: media.filename,
contentType: media.contentType,
publicUrl: media.publicUrl,
};
Resolves the serialization error in the manager when the Media property is public.. I'll push this fix to a new branch so you can test it."
And soon after,
"Please test with the branch https://github.com/PiranhaCMS/piranha.core/tree/features/make-mediafield-media-public"
I still must add the [JsonProperty] attribute to the Media field in MediaFieldBase class, so my deserializer can access the internal set method. Hakan's fix makes it so this attribute doesn't break the manager.
Thank you for your quick response Hakan, you rock!
We are trying to move from server session (IIS) to Redis-backed session. I updated my web.config with the custom sessionState configuration. I'm finding that only SOME of my key/value pairs are being saved. Of the 5 I expect to be in there, there are only 2. I verified all my code is ultimately hitting HttpContext.Current.Session.Add. I verified that my POCOs are marked as serializable. Looking at monitoring, I see that it adds the first two pairs, then everything after that just doesn't make it. No hit, no rejection, no exceptions. Nothing.
Anyone ever see this? Know where I could start to look to resolve?
TIA,
Matt
Update 1: I've switched to using a JSON serializer to store my data. Same thing. Doesn't seem to be a serialization issue.
Update 2: I've now downloaded the source code, compiled and am debugging it. The method SetAndReleaseItemExclusive, which seems to send the session items to Redis, is only hit once, though it should be hit more than once as my web site handle SSO and bounces from page to page to load the user and such. Have to investigate why it's only firing once...
Figured it out. Turns out that my AJAX request to an "API" endpoint without my MVC app did not have the appropriate session state attached. Therefore, the SetAndReleaseItemExclusive was never called. Adding this fixed it:
protected void Application_PostAuthorizeRequest()
{
if (HttpContext.Current.Request.Url.LocalPath == "/api/user/load")
HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
else
HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
}
I wrote the following MVC Controller to test cancellation functionality:
class MyController : Controller
{
[HttpGet("api/CancelTest")]
async Task<IActionResult> Get()
{
await Task.Delay(1000);
CancellationToken token = HttpContext.RequestAborted;
bool cancelled = token.IsCancellationRequested;
logger.LogDebug(cancelled.ToString());
return Ok();
}
}
Say, I want to cancel the request, so the value 'true' is logged in the controller action above. This is possible server-side if the server implements the IHttpRequestLifetimeFeature. Luckily Kestrel does, and this can be accomplished the following way:
var feature = (IHttpRequestLifetimeFeature) HttpContext.Features[typeof(IHttpRequestLifetimeFeature)];
feature.Abort();
The problem however is that I want to cancel the request on the client side. For example, in the browser. In pre-core versions of ASP.NET MVC/WebApi the cancellation token would automatically be cancelled if the browser aborted a request. Example: refresh the page a couple of times in Chrome. In the Network tab of the chrome dev tools you can now see the previous (unfinished) request be cancelled.
The thing is: in ASP.NET Core running on Kestrel, I can only see the following entry in the log:
Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException:
Error -4081 ECANCELED operation canceled
So the abort request from the browser DOES arrive and is handled by the Kestrel webserver. It does however not affect the RequestAborted property of the HttpContext in the controller, because the value 'false' is still logged by the method.
Question:
Is there a way to abort/cancel my controller's method, so that the HttpContext.RequestAborted property will be marked as cancelled?
Perhaps I can make something that would subscribe to Kestrel's operation cancelled trigger and call the IHttpRequestLifetimeFeature.Abort() method?
Update:
I did some further testing and it seems the HttpRequest IS in fact aborted, but there seems to be some kind of delay before the cancellation actually takes place. The delay is not time-factored, and seems to come straight from libuv (the library where the Kestrel webserver is build on top of). I posted more info on https://github.com/aspnet/KestrelHttpServer/issues/1103
More updates:
Issue has been moved to another one, because the previous one contained multiple problems. https://github.com/aspnet/KestrelHttpServer/issues/1139
Turns out that that simply using HttpContext.RequestAborted is indeed the right way, but due to a bug in Kestrel (the order in which FIN/RST packages were handled), the request was not aborted on a browser abort.
The bug should finally be fixed in Kestrel 2.0.
See the updates in my question for more information.
I get the following error within Magento CE 1.6.1.0
Warning: session_start() [<a href='function.session-start'>function.session-start</a>]: Cannot send session cookie - headers already sent by (output started at /home/dev/env/var/www/user/dev/wdcastaging/lib/Zend/Controller/Response/Abstract.php:586) in /home/dev/env/var/www/user/dev/wdcastaging/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php on line 119
when accessing /api/soap/?wsdl
Apparently, a session_start() is being attempted after the entire contents of the WSDL file have already been output, resulting in the error.
Why is magento attempting to start a session after outputting all the datums? I'm glad you asked. So it looks like controller_front_send_response_after is being hooked by Mage_Persistent in order to call synchronizePersistentInfo(), which in turn ends up getting that session_start() to fire.
The interesting thing is that this wasn't always happening, initially the WSDL loaded just fine for me, initially I racked my brains to try and see what customization may have been made to our install to cause this, but the tracing I've done seems to indicate that this is all happening entirely inside of core.
We have also experienced a tiny bit of (completely unrelated) strangeness with Mage_Persistent which makes me a little more willing to throw my hands up at this point and SO it.
I've done a bit of searching on SO and have found some questions related to the whole "headers already sent" thing in general, but not this specific case.
Any thoughts?
Oh, and the temporary workaround I have in place is simply disabling Mage_Persistent via the persistent/options/enable config data. I also did a little bit of digging as to whether it might be possible to observe an event in order to disable this module only for the WSDL controller (since that seems to be the only one having problems), but it looks like that module relies exclusively on this config flag to determine it's enabled status.
UPDATE: Bug has been reported: http://www.magentocommerce.com/bug-tracking/issue?issue=13370
I'd report this is a bug to the Magento team. The Magento API controllers all route through standard Magento action controller objects, and all these objects inherit from the Mage_Api_Controller_Action class. This class has a preDispatch method
class Mage_Api_Controller_Action extends Mage_Core_Controller_Front_Action
{
public function preDispatch()
{
$this->getLayout()->setArea('adminhtml');
Mage::app()->setCurrentStore('admin');
$this->setFlag('', self::FLAG_NO_START_SESSION, 1); // Do not start standart session
parent::preDispatch();
return $this;
}
//...
}
which includes setting a flag to ensure normal session handling doesn't start for API methods.
$this->setFlag('', self::FLAG_NO_START_SESSION, 1);
So, it sounds like there's code in synchronizePersistentInf that assumes the existence of a session object, and when it uses it the session is initialized, resulting in the error you've seen. Normally, this isn't a problem as every other controller has initialized a session at this point, but the API controllers explicitly turns it off.
As far as fixes go, your best bet (and probably the quick answer you'll get from Magento support) will be to disable the persistant cart feature for the default configuration setting, but then enable it for specific stores that need it. This will let carts
Coming up with a fix on your own is going to be uncharted territory, and I can't think of a way to do it that isn't terribly hacky/unstable. The most straight forward way would be a class rewrite on the synchronizePersistentInf that calls it's parent method unless you've detected this is an API request.
This answer is not meant to replace the existing answer. But I wanted to drop some code in here in case someone runs into this issue, and comments don't really allow for code formatting.
I went with a simple local code pool override of Mage_Persistent_Model_Observer_Session to exit out of the function for any URL routes that are within /api/*
Not expecting this fix to need to be very long-lived or upgrade-friendly, b/c I'm expecting them to fix this in the next release or so.
public function synchronizePersistentInfo(Varien_Event_Observer $observer)
{
...
if ($request->getRouteName() == 'api') {
return;
}
...
}