• 一步一步学习IdentityServer3 (6)


    上一个章节提到了数据持久化

    下面说一说自定义登录界面,Idr3提供了很多服务接口,其中一个就是 ViewService,我们只需要去注册 IViewService 这个接口的实现

    提供了一个泛型视图服务类继承了IViewService,网上能找到这个类我这里也贴出来

     public class IdSvrMvcViewService<TController> : IViewService
             where TController : ControllerBase
        {
            /// <summary>
            ///     We will use the DefaultViewService to do the brunt of our work
            /// </summary>
            private readonly DefaultViewService defaultViewService;
    
            private readonly DefaultViewServiceOptions config;
    
            private readonly ResourceCache cache = new ResourceCache();
    
            private readonly HttpContextBase httpContext;
    
            private readonly IControllerFactory controllerFactory;
    
            private readonly ViewEngineCollection viewEngineCollection;
    
            #region Constructors
    
            public IdSvrMvcViewService(HttpContextBase httpContext)
                : this(
                    httpContext,
                    new DefaultViewServiceOptions(),
                    new FileSystemWithEmbeddedFallbackViewLoader(),
                    ControllerBuilder.Current.GetControllerFactory(),
                    ViewEngines.Engines)
            {
            }
    
            public IdSvrMvcViewService(
                HttpContextBase httpContext,
                DefaultViewServiceOptions config,
                IViewLoader viewLoader,
                IControllerFactory controllerFactory,
                ViewEngineCollection viewEngineCollection)
            {
                this.httpContext = httpContext;
                this.config = config;
                this.defaultViewService = new DefaultViewService(this.config, viewLoader);
                this.controllerFactory = controllerFactory;
                this.viewEngineCollection = viewEngineCollection;
            }
    
            #endregion
    
            #region Override
    
         
            public Task<Stream> Login(LoginViewModel model, SignInMessage message)
            {
                return this.GenerateStream(
                    model,
                    message,
                    "login",
                    () => this.defaultViewService.Login(model, message));
            }
    
            public Task<Stream> Logout(LogoutViewModel model, SignOutMessage message)
            {
                return this.GenerateStream(
                    model,
                    "logout",
                    () => this.defaultViewService.Logout(model, message));
            }
    
          
            public Task<Stream> LoggedOut(LoggedOutViewModel model, SignOutMessage message)
            {
                return this.GenerateStream(
                     model,
                     "loggedOut",
                     () => this.defaultViewService.LoggedOut(model, message));
            }
    
           
            public Task<Stream> Consent(ConsentViewModel model, ValidatedAuthorizeRequest authorizeRequest)
            {
                return this.GenerateStream(
                    model,
                    "consent",
                    () => this.defaultViewService.Consent(model, authorizeRequest));
            }
    
          
            public Task<Stream> ClientPermissions(ClientPermissionsViewModel model)
            {
                // For some reason, this is referred to as "Permissions" instead of "ClientPermissions" in Identity Server.
                // This is hardcoded into their CSS so cannot be changed (unless you are overriding all their CSS)
                // This must also be in lower case for the same reason
                return this.GenerateStream(
                    model,
                    "permissions",
                    () => this.defaultViewService.ClientPermissions(model));
            }
    
          
            public Task<Stream> Error(ErrorViewModel model)
            {
                return this.GenerateStream(
                    model,
                    "error",
                    () => this.defaultViewService.Error(model));
            }
    
            #endregion
    
            #region Generate Stream
    
         
            private Task<Stream> GenerateStream<TViewModel, TMessage>(
                TViewModel model,
                TMessage message,
                string actionName,
                Func<Task<Stream>> fallbackFunc)
                where TViewModel : CommonViewModel
                where TMessage : Message
            {
                var result = this.GenerateHtml(actionName, model, message);
    
                // If we've not been able to generate the HTML, use the fallbackFunc to do so
                if (string.IsNullOrWhiteSpace(result))
                {
                    if (fallbackFunc != null)
                    {
                        return fallbackFunc();
                    }
    
                    return Task.FromResult(this.StringToStream(string.Empty));
                }
    
                return this.Render(model, actionName, result);
            }
    
          
            private Task<Stream> GenerateStream<TViewModel>(
                TViewModel model,
                string actionName,
                Func<Task<Stream>> fallbackFunc)
                where TViewModel : CommonViewModel
            {
                return GenerateStream<TViewModel, Message>(model, null, actionName, fallbackFunc);
            }
    
            
            private string GenerateHtml(
                string actionName,
                object model = null,
                object message = null)
            {
                // We want to use Razor to render the HTML since that will allow us to reuse components accross the IdentityServer & root website
                // This is based on code found here:
                // http://weblog.west-wind.com/posts/2012/May/30/Rendering-ASPNET-MVC-Views-to-String
    
                // Find the controller in question
                var controllerName = typeof(TController).Name.ToLower().Replace("controller", String.Empty);
                var controller = this.FindController(controllerName) as TController;
    
                // If we were unable to find one
                if (controller == null)
                {
                    // Stop processing
                    return null;
                }
    
                // Create storage for the Html result
                var generatedContent = string.Empty;
    
                // Find the Action in Question
                var actionDescriptor = this.FindAction(controller, actionName, model, message);
    
                // If we were able to find one
                if (actionDescriptor != null)
                {
                    // Try to initiate an Action to generate the HTML
                    // this is never cached since the Action may render different HTML based on the model/message
                    generatedContent = this.ExecuteAction(controller, actionDescriptor, model, message);
                }
    
                // If we either haven't found an action that was useable,
                //  or the action did not return something we can use
                //  try rendering the Razor view directly
                if (string.IsNullOrWhiteSpace(generatedContent))
                {
                    // If caching is disabled, generate every time
                    if (!this.config.CacheViews)
                    {
                        generatedContent = this.LoadRazorViewDirect(controller, actionName);
                    }
                    else
                    {
                        // Otherwise, load the Razor view from the cache
                        generatedContent = this.cache.Read(actionName);
    
                        // If we've not found this in the cache...
                        if (string.IsNullOrWhiteSpace(generatedContent))
                        {
                            // generate now
                            generatedContent = this.LoadRazorViewDirect(controller, actionName);
    
                            // And store in the cache for next time
                            this.cache.Write(actionName, generatedContent);
                        }
                    }
                }
    
                // Regardless, release the controller now we're done
                ControllerBuilder.Current.GetControllerFactory().ReleaseController(controller);
    
                // And return any HTML we might have generated
                return generatedContent;
            }
    
            /// <summary>
            /// Locate a Controller with the given name in the current MVC context
            /// </summary>
            /// <param name="controllerName"></param>
            /// <returns></returns>
            private ControllerBase FindController(string controllerName)
            {
                // Create the appropriate Route Data
                var routeData = new RouteData();
                routeData.Values.Add("controller", controllerName);
    
                // FUTURE: Fake HttpContext
                //    We need to to generate a different httpContext every time so that when we execute
                //    the controller, it cannot accidentally manipulate or close this current (outer) request.
                //    However, by creating a new empty request we loose all the context information that
                //    the Controller will need to make its decisions.
                //    There are therefore only 2 options:
                //    (1) Uses a fake HttpContext
                //    THEN
                //        Calls to controller.Execute are always OK
                //    BUT
                //        Anything that requires context information, such as
                //            context.GetOverriddenUserAgent() OR
                //            fakeHttpContext.GetOwinContext()
                //        fails since the fake Request has lost all the info of the genuine Request
                //    (2) Use the current (real) HttpContext
                //    THEN
                //        All Headers, User Agents, Session etc can be accessed by the controller
                //    BUT
                //        If anything doesn't work during the Controller.Execute (i.e. the authentication 
                //        fails with a wrong password) then the UserService does something that closes the
                //        Request (presumably because it's controller thinks it has completed the request
                //        and issues a redirect to the Error page).  This results in the following error
                //        when the Error page tries to process:
                //         "This method or property is not supported after HttpRequest.GetBufferlessInputStream has been invoked."
                //    For now, we'll use a fake HttpContext for now, and copy all the
                //        the pertinent information from the genuine Request into the fake one.
                //        It's worth noting that (A) it uses reflection and will 
                //        therefore be slow, and (B) only the information I know that we need has been 
                //        copied accross - making it not future proof.  What if we change the code and 
                //        need access to a different Request property that we haven't copied to our fake context?
                Debug.Assert(this.httpContext.Request.Url != null, "httpContext.Request.Url != null");
                var fakeHttpRequest = new HttpRequest(
                    null,
                    this.httpContext.Request.Url.ToString(),
                    this.httpContext.Request.Url.Query);
                var fakeHttpResponse = new HttpResponse(null);
                var fakeHttpContext = new HttpContext(fakeHttpRequest, fakeHttpResponse);
                var fakeHttpContextWrapper = new HttpContextWrapper(fakeHttpContext);
                var fakeRequestContext = new RequestContext(fakeHttpContextWrapper, routeData);
    
                // Needed for authentication
                foreach (var key in this.httpContext.Request.Cookies.AllKeys)
                {
                    var cookie = this.httpContext.Request.Cookies[key];
                    if (cookie != null)
                    {
                        fakeHttpRequest.Cookies.Set(cookie);
                    }
                }
    
                // Needed for "owin.environment"
                foreach (var key in this.httpContext.Items.Keys)
                {
                    fakeHttpContext.Items[key] = this.httpContext.Items[key];
                }
    
                // Needed for "User-Agent" in out DisplayModeProvider
                // From: http://stackoverflow.com/a/13307238
                var t = this.httpContext.Request.Headers.GetType();
                t.InvokeMember(
                    "MakeReadWrite",
                    BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    fakeHttpRequest.Headers,
                    null);
                t.InvokeMember(
                    "InvalidateCachedArrays",
                    BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    fakeHttpRequest.Headers,
                    null);
                foreach (var key in this.httpContext.Request.Headers.AllKeys)
                {
                    var item = new ArrayList { this.httpContext.Request.Headers[key] };
                    t.InvokeMember(
                        "BaseAdd",
                        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                        null,
                        fakeHttpRequest.Headers,
                        new object[] { key, item });
                }
    
                t.InvokeMember(
                    "MakeReadOnly",
                    BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    fakeHttpRequest.Headers,
                    null);
    
                // Use the Controller Factory to find the relevant controller
                var controller = this.controllerFactory.CreateController(fakeRequestContext, controllerName) as ControllerBase;
    
                if (controller != null)
                {
                    // Setup the context
                    controller.ControllerContext = new ControllerContext(fakeHttpContextWrapper, routeData, controller);
                }
    
                return controller;
            }
    
            /// <summary>
            /// Find a specific Action in the located Controller based on the name supplied.
            /// Where multiple Actions of that name exist, use the version that matches the maximum number of available/applicable parameters.
            /// </summary>
            /// <param name="controller">The controller containing the action</param>
            /// <param name="actionName">The Action to find in the controller</param>
            /// <param name="model">The Model that should be passed to the Action if possible</param>
            /// <param name="message">The Message that should be passed to the Action if possible</param>
            /// <returns></returns>
            private ActionDescriptor FindAction(
                ControllerBase controller,
                string actionName,
                object model = null,
                object message = null)
            {
                // Now get the Metadata about the controller
                var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
    
                // List all matching actions
                var actionDescriptor = controllerDescriptor.GetCanonicalActions()
                    .Where(
                        ad =>
    
                            // that have the correct name AND
                            ad.ActionName.ToLower() == actionName.ToLower() &&
                            this.HasAcceptableParameters(ad, model, message))
    
                    // Now put the largest number of parameters first
                    .OrderByDescending(ad => ad.GetParameters().Count())
    
                    // And that the top one
                    .FirstOrDefault();
    
                // If we were able to find it
                if (actionDescriptor != null)
                {
                    // Add the action name into the RouteData
                    controller.ControllerContext.RouteData.Values.Add("action", actionName);
                }
    
                return actionDescriptor;
            }
    
            /// <summary>
            /// Inject the Model &amp; Message into the parameters that will be passed to this Action (if appropriate parameters are available).
            /// </summary>
            /// <param name="controller">The controller that contains this action</param>
            /// <param name="actionDescriptor">The Action in the Controller that is going to be executed</param>
            /// <param name="model">The Model that should be passed to the Action if possible</param>
            /// <param name="message">The Message that should be passed to the Action if possible</param>
            private void PopulateActionParameters(
                ControllerBase controller,
                ActionDescriptor actionDescriptor,
                object model = null,
                object message = null)
            {
                if (actionDescriptor.ControllerDescriptor.ControllerType != controller.GetType())
                {
                    throw new ArgumentException("actionDescriptor does not describe a valid action for the controller supplied");
                }
    
                if (!this.HasAcceptableParameters(actionDescriptor, model, message))
                {
                    throw new ArgumentException("actionDescriptor does not have valid parameters that can be populated");
                }
    
                // Extract the Actions Parameters
                var parameters = actionDescriptor.GetParameters();
    
                // Extract the parameters we're likely to be filling in
                var firstParam = actionDescriptor.GetParameters().FirstOrDefault();
                var lastParam = actionDescriptor.GetParameters().LastOrDefault();
    
                // If we're expecting 1, assign either the model or message
                if (parameters.Count() == 1 && (model != null || message != null) && firstParam != null)
                {
                    // If it's assignable from Model
                    if (model != null && firstParam.ParameterType.IsAssignableFrom(model.GetType()))
                    {
                        controller.ControllerContext.RouteData.Values[firstParam.ParameterName] = model;
                    }
                    else if (message != null)
    
                    // Don't need to double check this because the HasAcceptableParameters method has already done this
                    /* if (message != null && firstParam.ParameterType.IsAssignableFrom(message.GetType())) */
                    {
                        controller.ControllerContext.RouteData.Values[firstParam.ParameterName] = message;
                    }
                }
    
                // If we're expecting 2, assign both the correct way round
                else if (parameters.Count() == 2 && model != null && message != null && firstParam != null &&
                         lastParam != null)
                {
                    if (
                        firstParam.ParameterType.IsAssignableFrom(model.GetType()) &&
                        lastParam.ParameterType.IsAssignableFrom(message.GetType()))
                    {
                        controller.ControllerContext.RouteData.Values[firstParam.ParameterName] = model;
                        controller.ControllerContext.RouteData.Values[lastParam.ParameterName] = message;
                    }
                    else
    
                    // Don't need to double check this because the HasAcceptableParameters method has already done this
                    /* 
                        if (
                        firstParam.ParameterType.IsAssignableFrom(message.GetType()) &&
                        lastParam.ParameterType.IsAssignableFrom(model.GetType()))
                    */
                    {
                        controller.ControllerContext.RouteData.Values[firstParam.ParameterName] = message;
                        controller.ControllerContext.RouteData.Values[lastParam.ParameterName] = model;
                    }
                }
            }
    
            /// <summary>
            /// Execute this Action (injecting appropriate parameters are available).
            /// </summary>
            /// <param name="controller">
            /// The controller that contains this action
            /// </param>
            /// <param name="actionDescriptor">
            /// The Action in the Controller that is going to be executed
            /// </param>
            /// <param name="model">
            /// The Model that should be passed to the Action if possible
            /// </param>
            /// <param name="message">
            /// The Message that should be passed to the Action if possible
            /// </param>
            /// <returns>
            /// The <see cref="string"/>.
            /// </returns>
            private string ExecuteAction(
                ControllerBase controller,
                ActionDescriptor actionDescriptor,
                object model = null,
                object message = null)
            {
                // Populate the Action Parameters
                this.PopulateActionParameters(controller, actionDescriptor, model, message);
    
                // Whilst Capturing the output ...
                using (var it = new ResponseCapture(controller.ControllerContext.RequestContext.HttpContext.Response))
                {
                    // Execute the Action (this will automatically invoke any attributes)
                    (controller as IController).Execute(controller.ControllerContext.RequestContext);
    
                    // Check for valid status codes
                    // EDFUTURE: For now, we only accept methods that produce standard HTML,
                    // 302 Redirects and other possibly valid responses are ignored since we 
                    // don't need them at the moment and aren't coding to cope with them
                    if ((HttpStatusCode)controller.ControllerContext.RequestContext.HttpContext.Response.StatusCode == HttpStatusCode.OK)
                    {
                        // And return the generated HTML
                        return it.ToString();
                    }
    
                    return null;
                }
    
                // FUTURE: Fake HttpContext (continued...)
                // The code above assumes that a fake HttpContext is in use for this controller
                //      (controller as IController).Execute(controller.ControllerContext.RequestContext);
                // otherwise we have the problem described in my initial "Fake HttpContext" comments.
                // 
                // It possible instead to execute the Action directly to avoid the problem
                //  of filter performing unexpected manipulations to the Response and Request,
                //  by using this code:
                //      actionDescriptor.Execute(controller.ControllerContext, parameters);
                //  but of course, this means that our filters (such as our [SetDisplayMode] attribute
                //  are never run and thus, the result is not as expected.
                // 
                // As an alternative to, it may be possible to recreate the (controller as IController).Execute(...)
                //  ourselves, but bypass the specific filters that cause unexpected manipulations to the Response and Request.
                // 
                // from Controller.Execute:
                //      ActionInvoker.InvokeAction(ControllerContext, actionName)
                //  from ControllerActionInvoker.InvokeAction:
                //      FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
                //      
                //      // Check no authentication issues
                //      AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
                //      if (authenticationContext.Result == null)
                //      {
                //      
                //          // Check no authorization issues
                //          AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
                //          if (authorizationContext.Result == null)
                //          {
                //      
                //              // Validate the Request
                //              if (controllerContext.Controller.ValidateRequest)
                //              {
                //                  ValidateRequest(controllerContext);
                //              }
                //      
                //              // Run the Action
                //              IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
                //              ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
                //      
                //              // The action succeeded. Let all authentication filters contribute to an action result (to
                //              // combine authentication challenges; some authentication filters need to do negotiation
                //              // even on a successful result). Then, run this action result.
                //              AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor, postActionContext.Result);
                //              InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, challengeContext.Result ?? postActionContext.Result);
                // 
                //              ...
            }
    
            /// <summary>
            ///     Check that the Action has parameters that are acceptable, i.e. one of these:
            ///     No parameter
            ///     1 parameter that matches the model
            ///     1 parameter that matches the message
            ///     2 parameters matching the model &amp; message (or vice versa)
            /// </summary>
            /// <param name="actionDescriptor">The Action in the Controller that is going to be executed</param>
            /// <param name="model">The Model that should be passed to the Action if possible</param>
            /// <param name="message">The Message that should be passed to the Action if possible</param>
            /// <returns></returns>
            [System.Diagnostics.Contracts.Pure]
            private bool HasAcceptableParameters(
                ActionDescriptor actionDescriptor,
                object model = null,
                object message = null)
            {
                var parameters = actionDescriptor.GetParameters();
    
                // Has either no parameters OR
                if (!parameters.Any())
                {
                    return true;
                }
    
                // Has 1 parameter ...
                if (parameters.Count() == 1
                    && ( // ...that accepts either the Model or the Message (and we have a model or a message)
                    (model != null && parameters.First().ParameterType.IsAssignableFrom(model.GetType())) ||
                    (message != null && parameters.First().ParameterType.IsAssignableFrom(message.GetType()))))
                {
                    return true;
                }
    
                // Has 2 parameters ...
                if (parameters.Count() == 2 && model != null && message != null
                    && (( // ... where one accepts the Model and the other accepts the Message
                            parameters.First().ParameterType.IsAssignableFrom(model.GetType())
                            && parameters.Last().ParameterType.IsAssignableFrom(message.GetType()))
                        || (parameters.Last().ParameterType.IsAssignableFrom(model.GetType())
                            && parameters.First().ParameterType.IsAssignableFrom(message.GetType()))))
                {
                    return true;
                }
    
                return false;
            }
    
            /// <summary>
            ///     Temporarily replace the Response.Output with a StringWriter for the duration of the ResponseCaptures lifetime
            ///     Usage:
            ///     using (var rc = new ResponseCapture(controller.ControllerContext.RequestContext.HttpContext.Response))
            ///     {
            ///     ...
            ///     return rc.ToString();
            ///     }
            ///     From: http://approache.com/blog/render-any-aspnet-mvc-actionresult-to/
            /// </summary>
            private class ResponseCapture : IDisposable
            {
                private readonly HttpResponseBase response;
    
                private readonly TextWriter originalWriter;
    
                private StringWriter localWriter;
    
                public ResponseCapture(HttpResponseBase response)
                {
                    this.response = response;
                    this.originalWriter = response.Output;
                    this.localWriter = new StringWriter();
                    response.Output = this.localWriter;
                }
    
                public override string ToString()
                {
                    this.localWriter.Flush();
                    return this.localWriter.ToString();
                }
    
                public void Dispose()
                {
                    if (this.localWriter != null)
                    {
                        this.localWriter.Dispose();
                        this.localWriter = null;
                        this.response.Output = this.originalWriter;
                    }
                }
            }
    
            #endregion
    
            #region Load Razor view direct
    
            /// <summary>
            ///     Renders the "/views/controllername/actionname.cshtml" view (if it exists)
            ///     using the Razor ViewEngine but without passing it through the Controller action
            /// </summary>
            /// <param name="controller"></param>
            /// <param name="viewName"></param>
            /// <returns></returns>
            private string LoadRazorViewDirect(ControllerBase controller, string viewName)
            {
                // Find the appropriate View
                var viewResult = this.viewEngineCollection.FindView(controller.ControllerContext, viewName, null);
    
                // If we've been able to find one
                if (viewResult != null && viewResult.View != null)
                {
                    // Store the result in a StringWriter
                    using (var sw = new StringWriter())
                    {
                        // Setup the ViewContext
                        var viewContext = new ViewContext(
                            controller.ControllerContext,
                            viewResult.View,
                            controller.ViewData,
                            controller.TempData,
                            sw);
    
                        // Render the View
                        viewResult.View.Render(viewContext, sw);
    
                        // Dispose of the View
                        viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
    
                        // Output the resultant HTML string
                        return sw.GetStringBuilder().ToString();
                    }
                }
    
                return null;
            }
    
            #endregion
    
            #region Taken from IdentityServer3.Core.Services.Default.DefaultViewService
    
            /// <summary>
            ///     Render the Html in the same way that the DefaultViewService does
            /// </summary>
            /// <param name="model"></param>
            /// <param name="page"></param>
            /// <param name="html"></param>
            /// <param name="clientName"></param>
            /// <returns></returns>
            private Task<Stream> Render(
                CommonViewModel model,
                string page,
                string html,
                string clientName = null)
            {
                Uri uriSiteUrl;
                var applicationPath = string.Empty;
                if (Uri.TryCreate(model.SiteUrl, UriKind.RelativeOrAbsolute, out uriSiteUrl))
                {
                    if (uriSiteUrl.IsAbsoluteUri)
                    {
                        applicationPath = uriSiteUrl.AbsolutePath;
                    }
                    else
                    {
                        applicationPath = uriSiteUrl.OriginalString;
                        if (applicationPath.StartsWith("~/"))
                        {
                            applicationPath = applicationPath.TrimStart('~');
                        }
                    }
    
                    if (applicationPath.EndsWith("/"))
                    {
                        applicationPath = applicationPath.Substring(0, applicationPath.Length - 1);
                    }
                }
    
                var json = JsonConvert.SerializeObject(
                    model,
                    Formatting.None,
                    new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
    
                var additionalStylesheets = this.BuildTags(
                    "<link href='{0}' rel='stylesheet'>",
                    applicationPath,
                    this.config.Stylesheets);
                var additionalScripts = this.BuildTags("<script src='{0}'></script>", applicationPath, this.config.Scripts);
    
                var variables = new
                {
                    siteName = Encoder.HtmlEncode(model.SiteName),
                    applicationPath,
                    model = Encoder.HtmlEncode(json),
                    page,
                    stylesheets = additionalStylesheets,
                    scripts = additionalScripts,
                    clientName
                };
    
                html = Replace(html, variables);
    
                return Task.FromResult(this.StringToStream(html));
            }
    
            /// <summary>
            /// A helper method to repeat the generation of a formatted string for every value supplied
            /// </summary>
            /// <param name="tagFormat"></param>
            /// <param name="basePath"></param>
            /// <param name="values"></param>
            /// <returns></returns>
            private string BuildTags(
                string tagFormat,
                string basePath,
                IEnumerable<string> values)
            {
                if (values == null)
                {
                    return String.Empty;
                }
    
                var enumerable = values as string[] ?? values.ToArray();
                if (!enumerable.Any())
                {
                    return String.Empty;
                }
    
                var sb = new StringBuilder();
                foreach (var value in enumerable)
                {
                    var path = value;
                    if (path.StartsWith("~/"))
                    {
                        path = basePath + path.Substring(1);
                    }
    
                    sb.AppendFormat(tagFormat, path);
                    sb.AppendLine();
                }
    
                return sb.ToString();
            }
    
            #endregion
    
            #region Modified from SampleApp.CustomViewService
            // More helper methods to allow placeholders in the rendered Html
    
            /// <summary>
            /// Replace placeholders in the string that correspond to the keys in the dictionary with the values of those keys.
            /// </summary>
            /// <param name="value"></param>
            /// <param name="values"></param>
            /// <returns></returns>
            private static string Replace(string value, IDictionary<string, object> values)
            {
                foreach (var key in values.Keys)
                {
                    var val = values[key];
                    val = val ?? String.Empty;
                    if (val != null)
                    {
                        value = value.Replace("{" + key + "}", val.ToString());
                    }
                }
    
                return value;
            }
    
            /// <summary>
            /// Replace placeholders in the string that correspond to the names of properties in the object with the values of those properties.
            /// </summary>
            /// <param name="value"></param>
            /// <param name="values"></param>
            /// <returns></returns>
            private string Replace(string value, object values)
            {
                return Replace(value, this.Map(values));
            }
    
            /// <summary>
            /// Convert an object into a Dictionary by enumerating and listing its properties
            /// </summary>
            /// <param name="values"></param>
            /// <returns></returns>
            private IDictionary<string, object> Map(object values)
            {
                var dictionary = values as IDictionary<string, object>;
    
                if (dictionary == null)
                {
                    dictionary = new Dictionary<string, object>();
                    foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
                    {
                        dictionary.Add(descriptor.Name, descriptor.GetValue(values));
                    }
                }
    
                return dictionary;
            }
    
            /// <summary>
            /// Convert a stringto a Stream (containing the string)
            /// </summary>
            /// <param name="s"></param>
            /// <returns></returns>
            private Stream StringToStream(string s)
            {
                var ms = new MemoryStream();
                var sw = new StreamWriter(ms);
                sw.Write(s);
                sw.Flush();
                ms.Seek(0, SeekOrigin.Begin);
                return ms;
            }
    
            #endregion
        }

    接下来就是定义我们自己的Controller了

    我们新建了一个LoginController登录控制器,这个控制器就是普通的控制器,主要是Action参数需要注意,这里的Action参数都是与Idr3相关

     public class LoginController : Controller
        {
            public LoginController()
            {
            }
    
            #region Login
    
           
            public ActionResult Login(LoginViewModel model, SignInMessage message)
            {
                return this.View(model);
            }
    
            #endregion
    
            #region Logout
    
          
            public ActionResult Logout(LogoutViewModel model)
            {
              
                return this.View(model);
            }
    
            #endregion
    
            #region LoggedOut
    
         
            public ActionResult LoggedOut(LoggedOutViewModel model)
            {
    
              
                return this.View(model);
            }
    
            #endregion
    
            #region Consent
    
          
            public ActionResult Consent(ConsentViewModel model)
            {
                return this.View(model);
            }
    
            #endregion
    
            #region Permissions
    
          
            public ActionResult Permissions(ClientPermissionsViewModel model)
            {
                return this.View(model);
            }
    
            #endregion
    
            #region Error
    
            public virtual ActionResult Error(ErrorViewModel model)
            {
                return this.View(model);
            }
    
            #endregion
        }
    LoginController

    Idr3提供了相关的视图模型,针对每个步骤 LoginViewModel 、LogoutViewModel、LoggedOutViewModel、ConsentViewModel、ClientPermissionsViewModel、ErrorViewModel 进一步了解可以看下里面每个字段的作用

      factory.ViewService = new Registration<IViewService, IdrConfig.IdSvrMvcViewService<Controllers.LoginController>>();

    这样自定义视图就搞定了,不光是登录,授权允许页 退出等等,只需要加入相对应的cshtml mvc 页面就ok了,了解每个模型知道里面字段作用对相关交互很重要

  • 相关阅读:
    使用 RMAN 同步数据库
    关于MongoDB分布式高可用集群实现
    Oracle排错总结
    Oracle中Restore和Recovery的区别
    linux下防火墙iptables原理及使用
    RHEL6.5上升级OpenSSH7.4p1
    awk中next以及getline用法示例
    Linux下Redis的安装和部署
    Mongodb集群搭建的三种方式
    天地图服务地址
  • 原文地址:https://www.cnblogs.com/liyouming/p/7514964.html
Copyright © 2020-2023  润新知