Tech Off Thread

16 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

How to catch exceptions raised by Custom Membership provider?

Back to Forum: Tech Off
  • User profile image
    sunny.mohan

    Hi,

    This is another post regarding the problem I am facing with handling exceptions raised by Custom Membership Provider.

    I have written a class "CustomMemberProvider" which throws an exception through its ValidateUser() method. I would like to rethrow this excepiton from the catch block of the ValidateUser method in order to handle it within the .aspx page and display user an appropriate message.

    When I try to throw an exception in the catch block of ValidateUser() method, it halts the execution there and does go back to the .aspx page as expected.


    Please help.

    Regards,
    Sunny

    http://www.mohanenterprises.com


  • User profile image
    wisemx
  • User profile image
    sunny.mohan

    It doesn't help much. My problem is very specific. I hope I am able put the question in a right way in my post.

    The problem I am facing is that in case I want to capture the exception raised by ValidateUser method of the custom membership control, how is that possible? Because when I try to throw an exception from the ValidateUser() method of my custom membership class, it doesn't get caught by .aspx page. Rather the execution halts in my custom membership project itself.

    PS: I have implemented custom membership class in a separate project which is part of the same solution.

    --Sunny

  • User profile image
    andrewmyhre

    Could you post some code to illustrate what's happening more clearly?

    Your membership provider should inherit from IMembershipProvider. The IMembershipProvider contract specifies that ValidateUser() returns true or false, and an exception is raised in exceptional cases, like if the data store isn't set up properly. This makes sense and controls which use your membership provider aren't built to handle exceptional cases. The power and reusability of the IMembershipProvider is that it always behaves the same way, no matter how it works under the hood.

    When the login control calls ValidateUser() it expects a bool to be returned, at which point it raises its events (which you can hook into) and sets your auth cookie. If you throw an exception from ValidateUser you won't be able to catch it because it will bubble up to code within the login control that you don't have access to. If you write a new control inheriting from Login, you could override the method which calls ValidateUser(), though. You'll have to investigate which method it is.

    But generally, don't throw an exception from ValidateUser() unless you want the application to stop entirely and show the exception to the user.

    Raising exceptions isn't a good way to perform conditional logic anyway though. Maybe there's a better way?  Can you explain why you need to raise an exception, and we should be able to think of a way to do it more easily.

    Hope this makes sense!

  • User profile image
    stevo_

    But ValidateUser isn't suposed to throw exceptions (other than if something really wrong happened (in which case it SHOULD bubble))..

    Your method should be purely returning true or false to tell the caller if the username and password they supplied were correct..

    In terms of using the login control, you aren't catching the exception because you aren't the one calling ValidateUser(..), the login control is doing this..

    If you want to catch exceptions on a page then you should handle the Page.Error event, or if you want to catch them globally (for the entire site) you should catch them in Application_Error in the global asax..

    Above this, theres error catching in asp.net anyway, so you can display error pages in the event of errors.

  • User profile image
    wisemx

    Hi Sunny,
     I work with the ASP.NET 2.0 membership provider a lot and am having somewhat of a hard time seeing where you are running into trouble with this.
    Still want to help you however...
    See if this MSDN article does the trick:
    http://msdn2.microsoft.com/en-us/magazine/cc163703.aspx
      Salute,
        Mark

  • User profile image
    erik_

    How are you calling the membership validateuser? thru the asp:login control or just from the code behind?

    try {
       
    Membership.ValidateUser("erik", "test");
    }
    catch (Exception ex) {

    }

    should catch the exception thrown from validateuser

  • User profile image
    sunny.mohan

    Inherits System.Web.Security.MembershipProvider

    ======================================

     

    public class CustomMembershipProvider : System.Web.Security.MembershipProvider

     

    ================================================ 

     

    This is how the ValidateUser() method looks like

    =====================================

    public override bool ValidateUser(string username, string password)

    {

     

        try

        {

            throw new Exception("The method or operation is not yet implemented.");

        }

     

        catch (Exception ex)

        {

     

        throw ex;

        }

    }

     

    In the code above, I am intentionally throwing an exception to see how it can be handled in an aspx page. I didn't intend to throw an exception like this but I accidently ran the code without removing these default exception statements and it got stuck there. Since then I have been trying to find a solution. Never thought that it's going to take so long. Therefore, there is no specific reason to raise this exception. I could have easily moved forward and implemented the method. But I am now curious to find a solution for it.

     

    The code above lies in a separate project, which is part of the same solution as the web site project where I am intending to handle the exception.

     

    Andrew - Thanks for explaining it in such a nice way. As you rightly said that login control being a composite control, I might need to capture the event using Bubbleevent functionality. I have been trying to find that out as well...Let's see if I get any success. But do you think I would need to override the login control as well to capture these exceptions? I couldn't really figure out as how will that work. If you can, then please expound on it a bit.

     

     Stevo - Thanks to you as well. I think we all are very close in terms on our understanding. Probably you guys might be having a better idea abt it. I am still trying it as and when I am getting time in between my regular work routine.

     

    Shall update you soon abt any progress.

     

     Wisemx - I hope the code that I have provided above gives some idea of what I am talking abt. However, if you still seek any clarification, please revert back and I shall try to explain it in a better way.

     

     Erik - I have put the code, so let me know if you want to know anything additional.

     

    Guys, plz do inform me in case you know abt a specific method that can handle this event in aspx. I am also trying to locate the same.

     

    --Sunny

      www.mohaneneterpises.com

  • User profile image
    sunny.mohan

    Thanks for your response Erik.

    Well, if I have to redirect the user from the ValidateUser() method, I can achieve that within the CustomMembership provider class as well.

    But I want to find out a technique to throw an exception from the CustomMembership class to handle it in the aspx page itself.

    In other words, I want to explore the other two options suggested by Stevo and Andrew where they talk about bubbling up the events off the login control. I want to handle it in a manner as we handle any other errors raised by a typical Business layer.

    In a situation wherein for some reason an error (DB connection or any other error related to DB) is encountered by the ValidateUser() method so in that scenario it should be able to handle it just like any other error raised by a typical Business layer. The errors which are raised by classes in a business layer are usually handled in a proper manner through the aspx pages. So that's what I want to achieve. Doing it through Global.asax isn't a usual way of handling errors.

    I hope it helps everyone understand my intentions as why I wish to do it in this particular way.


    www.mohanenterprises.com

  • User profile image
    erik_

    protected void Page_Load(object sender, EventArgs e) {
       Page.Error +=
    new EventHandler(Page_Error);
    }

    void Page_Error(object sender, EventArgs e) {
       Response.Redirect(
    http://www.google.com);
    }

    is the same as what happends in the global.asax only than on page level. Still it catches all the exceptions happening on the page. Using the global.asax is a perfect way for this if you ask me. You are able to display a generic help message and mail home or log the exception for every exception in the webapplication. Than you at least known that it happends for each page in your webapplication, instead of only the ones that you didn't forget to add the above.

    An other option is to create your own implementation or inherit the asp.net login control and add the exception handling.


    Don't forget, Exceptions are for exceptional problems. Not to control the flow..

  • User profile image
    erik_

    public class Login2 : System.Web.UI.WebControls.Login {

       protected override void OnAuthenticate (System.Web.UI.WebControls.AuthenticateEventArgs e) {
          try {
             base.OnAuthenticate(e); 
          }
    catch (Exception ex) { 
             HttpContext.Current.Response.Write("ex: " + ex.Message);
             // or HttpContext.Response.Redirect("http://www.google.com");
          }
       }
    }

    this is the other option...

    I hope any of these is what you are looking for, because I think I don't fully understand your needs.

  • User profile image
    erik_

    You can't catch the Exception the way you want it, with the default login control as far as I know.

    What you can do is create a global.asax file and catch all exceptions (or check if the exception is a membershipprovider exception) and do something like:

    <%@ Application Language="C#" %>

    <script runat="server">

    void Application_Error(object sender, EventArgs e) {

       // Code that runs when an unhandled error occurs
       
    Response.Redirect(http://www.google.com);
    }

    </script>

  • User profile image
    sunny.mohan

    Thanks for your suggestions Erick. I appreciate it.

    Actually it's not that what you're suggesting I am not convinced with that.

    Can you please take a look at the code below? This is how my code looks like.

    =========================
    ASPX page code:
    =========================


    <body onload="fnSetFocus();">

    <form id="form1" runat="server">

    <asp:ScriptManager ID="ScriptManager1" runat="server" />

    <div>

    <asp:Login ID="Login1" runat="server" BackColor="#F7F6F3" BorderColor="#E6E2D8" BorderPadding="4" BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" Font-Size="0.8em" ForeColor="#333333" MembershipProvider="CustomMembershipProviderLib.CustomMembershipProvider" OnAuthenticate="Login1_Authenticate" >

    <TextBoxStyle Font-Size="0.8em" />

    <LoginButtonStyle BackColor="#FFFBFF" BorderColor="#CCCCCC" BorderStyle="Solid" BorderWidth="1px"

    Font-Names="Verdana" Font-Size="0.8em" ForeColor="#284775" />

    <InstructionTextStyle Font-Italic="True" ForeColor="Black" />

    <TitleTextStyle BackColor="#5D7B9D" Font-Bold="True" Font-Size="0.9em" ForeColor="White" />

    </asp:Login>

    </div>

    </form>

    </body>


    ===========================
    Code Behind code:
    ======================


    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)

    {

    try

    {

    CustomMembershipProvider objCMP = new CustomMembershipProvider();

    e.Authenticated = objCMP.ValidateUser(Login1.UserName, Login1.Password);

    objCMP = null;

    }

    catch (Exception ex)

    {

    Login1.FailureText = ex.Message;

    }

    finally

    {

    Login1.Dispose();

    }

    }




    ====================================
    CustomMembershipProvider.cs code
    ====================================

    public override bool ValidateUser(string username, string password)

    {

    try

    {

    throw new Exception("The method or operation is not yet implemented.");

    }

    catch (Exception ex)

    {

    throw ex;

    }

    }






    This is how my code looks like now and it seems to achieve what I wanted to. But I still wanted to do it in a little different way. Even though I am using a CustomMembershipProvider class but I had to create an object of that class to be able to capture its exceptions.

    What I wanted was that I should be able to do it without writing any code in the aspx or I should say without explicitly making an object of the CustomMembershipProvider class.

    I understand that doing it in such a way might not bring any value but to have a better understanding of the whole process, I wanted to do it in this way.

    I hope that brings some clarity on what I intend to do.

    --Sunny
    www.mohanenterprises.com

  • User profile image
    erik_

    You should never need to call the custom membership provider directly. To let the provider pattern make sense you should only talk to your custom membership provider thrue the membership object.

    By directly calling the custom membership provider object you are not able to switch membership providers in the membership web.config section.

    Just to make sure you understand my last post, this is what that gives in aspx modified to your last code snipped. This will do the same as the code you have only than catch the exception and display it into the failuretext.

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
    <%
    @ Register Assembly="member" Namespace="member" TagPrefix="asp" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <
    html xmlns="http://www.w3.org/1999/xhtml">
    <
    head runat="server">
    <title>Untitled Page</title>
    </
    head>
    <
    body>
    <form id="form1" runat="server">
       <div>
          <asp:Login2 ID="Login2" runat="server"></asp:Login2>
       </div>
    </form>
    </
    body>
    </
    html>


    public class Login2 : System.Web.UI.WebControls.Login {

    protected
    override void OnAuthenticate(System.Web.UI.WebControls.AuthenticateEventArgs e) { 
       try {
          base.OnAuthenticate(e);
       }
    catch (Exception ex) {
          this.FailureText = ex.Message;
          this.OnLoginError(EventArgs.Empty);
       }
    }
    }

  • User profile image
    sunny.mohan

    I do agree with you on your thoughts in the first paragraph. But When you say "Membership" object, Will that be a login control in this case?

    Secondly, I couldn't really make out what you mean by switching membership providers. Do you mean that then in that case the login control by itself won't be able to refer/recognize the custom membership provider defined in the web.config file.

    Also, another thing that I noticed in your aspx code section is that you're missing the MembershipProvider property tag in the login control attributes. I was wondering that how would the system recognize the custom provider without the specification of Membership provider in the login control attributes.

    Another question, where am I supposed to put the below code snippet? In the code-behind file? Can you paste the whole code of your code behind file which would have such code?

    public class Login2 : System.Web.UI.WebControls.Login {

    protected override void OnAuthenticate(System.Web.UI.WebControls.AuthenticateEventArgs e) {
       try {
          base.OnAuthenticate(e);
       } catch (Exception ex) {
          this.FailureText = ex.Message;
          this.OnLoginError(EventArgs.Empty);
       }
    }
    }

    --Sunny
    www.mohanenterprises.com

  • User profile image
    erik_

    Maby it is more clear if you just see the code. Here is my solution file: sample_membership_provider.zip (vs 2008 solution)

    Let me know if there still is anything unclear after looking at the project.


    Edit: updated project, forgot to add website. You probaly need to readd the website with the correct path, because it points to my f:\projects\test folder instead of the current folder.

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.