private void EnsureServices(HttpContext context)
        {
            if (_servicesRetrieved)
            {
                return;
            }

            var services = context.RequestServices;

            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(services);

            // The IActionContextAccessor is optional. We want to avoid the overhead of using CallContext
            // if possible.
            _actionContextAccessor = services.GetService <IActionContextAccessor>();

            _actionInvokerFactory = services.GetRequiredService <IActionInvokerFactory>();
            _actionSelector       = services.GetRequiredService <IActionSelector>();
            _diagnosticSource     = services.GetRequiredService <DiagnosticSource>();

            var factory = services.GetRequiredService <ILoggerFactory>();

            _logger = factory.CreateLogger <MvcRouteHandler>();

            _servicesRetrieved = true;
        }
Exemple #2
0
        /// <summary>
        /// Adds MVC to the <see cref="IApplicationBuilder"/> request execution pipeline.
        /// </summary>
        /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
        /// <param name="configureRoutes">A callback to configure MVC routes.</param>
        /// <returns>A reference to this instance after the operation has completed.</returns>
        public static IApplicationBuilder UseMvc(
            this IApplicationBuilder app,
            Action <IRouteBuilder> configureRoutes)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }

            if (configureRoutes == null)
            {
                throw new ArgumentNullException(nameof(configureRoutes));
            }

            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

            var routes = new RouteBuilder
            {
                DefaultHandler  = new MvcRouteHandler(),
                ServiceProvider = app.ApplicationServices
            };

            configureRoutes(routes);

            // Adding the attribute route comes after running the user-code because
            // we want to respect any changes to the DefaultHandler.
            routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
                                     routes.DefaultHandler,
                                     app.ApplicationServices));

            return(app.UseRouter(routes.Build()));
        }
Exemple #3
0
        public async Task RouteAsync([NotNull] RouteContext context)
        {
            var services = context.HttpContext.RequestServices;

            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(services);

            EnsureLogger(context.HttpContext);
            var actionSelector   = services.GetRequiredService <IActionSelector>();
            var actionDescriptor = await actionSelector.SelectAsync(context);

            if (actionDescriptor == null)
            {
                _logger.LogVerbose("No actions matched the current request.");
                return;
            }

            // Replacing the route data allows any code running here to dirty the route values or data-tokens
            // without affecting something upstream.
            var oldRouteData = context.RouteData;
            var newRouteData = new RouteData(oldRouteData);

            if (actionDescriptor.RouteValueDefaults != null)
            {
                foreach (var kvp in actionDescriptor.RouteValueDefaults)
                {
                    if (!newRouteData.Values.ContainsKey(kvp.Key))
                    {
                        newRouteData.Values.Add(kvp.Key, kvp.Value);
                    }
                }
            }

            try
            {
                context.RouteData = newRouteData;

                using (_logger.BeginScope("ActionId: {ActionId}", actionDescriptor.Id))
                {
                    _logger.LogVerbose("Executing action {ActionDisplayName}", actionDescriptor.DisplayName);

                    await InvokeActionAsync(context, actionDescriptor);

                    context.IsHandled = true;
                }
            }
            finally
            {
                if (!context.IsHandled)
                {
                    context.RouteData = oldRouteData;
                }
            }
        }
Exemple #4
0
        public void MvcServicesHelperDoesNotThrowIfServiceExists()
        {
            // Arrange
            var services       = new Mock <IServiceProvider>();
            var expectedOutput = new MvcMarkerService();

            services.Setup(o => o.GetService(typeof(MvcMarkerService)))
            .Returns(expectedOutput);

            // Act & Assert (does not throw)
            MvcServicesHelper.ThrowIfMvcNotRegistered(services.Object);
        }
Exemple #5
0
        public void MvcServicesHelperDoesNotThrowIfServiceExists()
        {
            // Arrange
            var services       = new Mock <IServiceProvider>();
            var expectedOutput = new MvcMarkerService();

            services.Setup(o => o.GetService(typeof(IEnumerable <MvcMarkerService>)))
            .Returns(new List <MvcMarkerService> {
                expectedOutput
            });

            // Act & Assert
            Assert.DoesNotThrow(() => MvcServicesHelper.ThrowIfMvcNotRegistered(services.Object));
        }
Exemple #6
0
        public void MvcServicesHelperThrowsIfServiceIsAbsent()
        {
            // Arrange
            var services = new Mock <IServiceProvider>();

            services.Setup(o => o.GetService(typeof(IEnumerable <MvcMarkerService>)))
            .Returns(new List <MvcMarkerService>());
            var expectedMessage = "Unable to find the required services. Please add all the required " +
                                  "services by calling 'IServiceCollection.AddMvc()' inside the call to 'IApplicationBuilder.UseServices(...)' " +
                                  "or 'IApplicationBuilder.UseMvc(...)' in the application startup code.";

            // Act & Assert
            var ex = Assert.Throws <InvalidOperationException>(
                () => MvcServicesHelper.ThrowIfMvcNotRegistered(services.Object));

            Assert.Equal(expectedMessage, ex.Message);
        }
Exemple #7
0
        public static IApplicationBuilder UseDebby(
            this IApplicationBuilder app,
            Action <IRouteBuilder> configureRoutes)
        {
            MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

            var routes = new RouteBuilder
            {
                DefaultHandler  = new MvcRouteHandler(),
                ServiceProvider = app.ApplicationServices
            };

            routes.Routes.Add(AttributeRouting.CreateAttributeMegaRoute(
                                  routes.DefaultHandler,
                                  app.ApplicationServices));

            configureRoutes(routes);

            return(app.UseRouter(routes.Build()));
        }
Exemple #8
0
        public static IApplicationBuilder UseMvc(
            [NotNull] this IApplicationBuilder app,
            [NotNull] Action <IRouteBuilder> configureRoutes)
        {
            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

            var routes = new RouteBuilder
            {
                DefaultHandler  = new MvcRouteHandler(),
                ServiceProvider = app.ApplicationServices
            };

            routes.Routes.Add(AttributeRouting.CreateAttributeMegaRoute(
                                  routes.DefaultHandler,
                                  app.ApplicationServices));

            configureRoutes(routes);

            return(app.UseRouter(routes.Build()));
        }
Exemple #9
0
        public async Task RouteAsync(RouteContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var services = context.HttpContext.RequestServices;

            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(services);
            EnsureServices(context.HttpContext);

            var actionDescriptor = await _actionSelector.SelectAsync(context);

            if (actionDescriptor == null)
            {
                _logger.NoActionsMatched();
                return;
            }

            // Replacing the route data allows any code running here to dirty the route values or data-tokens
            // without affecting something upstream.
            var oldRouteData = context.RouteData;
            var newRouteData = new RouteData(oldRouteData);

            if (actionDescriptor.RouteValueDefaults != null)
            {
                foreach (var kvp in actionDescriptor.RouteValueDefaults)
                {
                    if (!newRouteData.Values.ContainsKey(kvp.Key))
                    {
                        newRouteData.Values.Add(kvp.Key, kvp.Value);
                    }
                }
            }

            // Removing RouteGroup from RouteValues to simulate the result of conventional routing
            newRouteData.Values.Remove(AttributeRouting.RouteGroupKey);

            try
            {
                context.RouteData = newRouteData;

                _diagnosticSource.BeforeAction(actionDescriptor, context.HttpContext, context.RouteData);

                using (_logger.ActionScope(actionDescriptor))
                {
                    _logger.ExecutingAction(actionDescriptor);

                    var startTime = Environment.TickCount;
                    await InvokeActionAsync(context, actionDescriptor);

                    context.IsHandled = true;

                    _logger.ExecutedAction(actionDescriptor, startTime);
                }
            }
            finally
            {
                _diagnosticSource.AfterAction(actionDescriptor, context.HttpContext, context.RouteData);

                if (!context.IsHandled)
                {
                    context.RouteData = oldRouteData;
                }
            }
        }
Exemple #10
0
        public async Task RouteAsync([NotNull] RouteContext context)
        {
            var services = context.HttpContext.RequestServices;

            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(services);

            Contract.Assert(services != null);

            // TODO: Throw an error here that's descriptive enough so that
            // users understand they should call the per request scoped middleware
            // or set HttpContext.Services manually

            EnsureLogger(context.HttpContext);
            using (_logger.BeginScope("MvcRouteHandler.RouteAsync"))
            {
                var actionSelector   = services.GetService <IActionSelector>();
                var actionDescriptor = await actionSelector.SelectAsync(context);

                if (actionDescriptor == null)
                {
                    if (_logger.IsEnabled(TraceType.Information))
                    {
                        _logger.WriteValues(new MvcRouteHandlerRouteAsyncValues()
                        {
                            ActionSelected = false,
                            ActionInvoked  = false,
                            Handled        = context.IsHandled
                        });
                    }

                    return;
                }

                if (actionDescriptor.RouteValueDefaults != null)
                {
                    foreach (var kvp in actionDescriptor.RouteValueDefaults)
                    {
                        if (!context.RouteData.Values.ContainsKey(kvp.Key))
                        {
                            context.RouteData.Values.Add(kvp.Key, kvp.Value);
                        }
                    }
                }

                var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor);

                var optionsAccessor = services.GetService <IOptionsAccessor <MvcOptions> >();
                actionContext.ModelState.MaxAllowedErrors = optionsAccessor.Options.MaxModelValidationErrors;

                var contextAccessor = services.GetService <IContextAccessor <ActionContext> >();
                using (contextAccessor.SetContextSource(() => actionContext, PreventExchange))
                {
                    var invokerFactory = services.GetService <IActionInvokerFactory>();
                    var invoker        = invokerFactory.CreateInvoker(actionContext);
                    if (invoker == null)
                    {
                        var ex = new InvalidOperationException(
                            Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
                                actionDescriptor.DisplayName));

                        // Add tracing/logging (what do we think of this pattern of
                        // tacking on extra data on the exception?)
                        ex.Data.Add("AD", actionDescriptor);

                        if (_logger.IsEnabled(TraceType.Information))
                        {
                            _logger.WriteValues(new MvcRouteHandlerRouteAsyncValues()
                            {
                                ActionSelected = true,
                                ActionInvoked  = false,
                                Handled        = context.IsHandled
                            });
                        }

                        throw ex;
                    }

                    await invoker.InvokeAsync();

                    context.IsHandled = true;

                    if (_logger.IsEnabled(TraceType.Information))
                    {
                        _logger.WriteValues(new MvcRouteHandlerRouteAsyncValues()
                        {
                            ActionSelected = true,
                            ActionInvoked  = true,
                            Handled        = context.IsHandled
                        });
                    }
                }
            }
        }
Exemple #11
0
        public async Task RouteAsync(RouteContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var services = context.HttpContext.RequestServices;

            // Verify if AddMvc was done before calling UseMvc
            // We use the MvcMarkerService to make sure if all the services were added.
            MvcServicesHelper.ThrowIfMvcNotRegistered(services);
            EnsureServices(context.HttpContext);

            var actionDescriptor = await _actionSelector.SelectAsync(context);

            if (actionDescriptor == null)
            { //没有直接找到对应的controller 下的 action, 转为动态视图查找
                //这里要不要把处理过的路径缓存起来,如果再次遇到相同的路径需要动态处理的则可以直接从缓存中获取视图文件名称
                var requestPath = context.HttpContext.Request.Path.Value ?? string.Empty;
                if (!string.IsNullOrEmpty(requestPath)) //只有请求路径不为空时进行动态处理
                {
                    int index = requestPath.IndexOf('?');
                    if (index > 0)
                    {
                        requestPath = requestPath.Replace(context.HttpContext.Request.QueryString.ToString(), "");//去掉url参数
                    }
                    requestPath = requestPath.TrimStart('/').ToLower();
                    List <string> paths    = new List <string>(requestPath.Split('/'));
                    string        viewName = "";
                    string        app      = paths[0];
                    string        ctr      = paths.Count > 1 ? paths[1] : "";

                    //根据路径构造视图名称
                    //匹配 /app 路径, 在后面默认加上index
                    if (paths.Count == 1)
                    {
                        paths.Add("index");
                    }
                    paths.Remove(app);//移除app的名称

                    //匹配 /app/*************后面的路径构造成视图文件名称, /app/后面可以接任意长度路径
                    viewName = string.Format("~/apps/{0}/template/page/{1}", app, string.Join("_", paths));

                    //修改成访问的动态视图 Controller 和 action
                    var routeData = new RouteData(context.RouteData);

                    //if (routeData.Values.ContainsKey(app)) routeData.Values.Remove("app");
                    routeData.Values.Clear();
                    routeData.Values.Add("app", "base");
                    //if (routeData.Values.ContainsKey("controller")) routeData.Values.Remove("controller");
                    routeData.Values.Add("controller", "common");
                    //if (routeData.Values.ContainsKey("action")) routeData.Values.Remove("action");
                    routeData.Values.Add("action", "dynamic");
                    //routeData.Values.Add("id", id);

                    context.HttpContext.Items.Add("__appname__", app);
                    context.HttpContext.Items.Add("__check__", ctr);
                    context.HttpContext.Items.Add("__dynamicviewname__", viewName);
                    context.RouteData = routeData; //覆盖原来要访问的controller 和 action


                    //再找一次(这次是找动态的视图controoller 和 action)
                    actionDescriptor = await _actionSelector.SelectAsync(context);
                }
            }

            // Replacing the route data allows any code running here to dirty the route values or data-tokens
            // without affecting something upstream.
            var oldRouteData = context.RouteData;
            var newRouteData = new RouteData(oldRouteData);

            if (actionDescriptor.RouteValueDefaults != null)
            {
                foreach (var kvp in actionDescriptor.RouteValueDefaults)
                {
                    if (!newRouteData.Values.ContainsKey(kvp.Key))
                    {
                        newRouteData.Values.Add(kvp.Key, kvp.Value);
                    }
                }
            }

            // Removing RouteGroup from RouteValues to simulate the result of conventional routing
            newRouteData.Values.Remove(AttributeRouting.RouteGroupKey);

            try
            {
                context.RouteData = newRouteData;

                _diagnosticSource.BeforeAction(actionDescriptor, context.HttpContext, context.RouteData);
                await InvokeActionAsync(context, actionDescriptor);//执行 action 方法

                context.IsHandled = true;

                //清除路由信息?貌似会带到下次请求,如果下次请求的路径比上次短,那么下次的路由信息将会出现脏信息
                context.RouteData.Values.Clear();  //這裡的值不能清除啊,有可能還要在內部的部分也用到
                //清楚保存的临时信息
                context.HttpContext.Items.Remove("__appname__");
                context.HttpContext.Items.Remove("__check__");
                context.HttpContext.Items.Remove("__dynamicviewname__");
            }
            catch (Exception ex)
            {
                Debug.WriteLine("===========excute the action exception:" + ex.Message);
            }
            finally
            {
                _diagnosticSource.AfterAction(actionDescriptor, context.HttpContext, context.RouteData);

                if (!context.IsHandled)
                {
                    context.RouteData = oldRouteData;
                }
            }
        }