diff --git a/src/RouteJs.AspNet/AttributeRouteFetcher.cs b/src/RouteJs.AspNet/AttributeRouteFetcher.cs
index ca87c83..d23b31f 100644
--- a/src/RouteJs.AspNet/AttributeRouteFetcher.cs
+++ b/src/RouteJs.AspNet/AttributeRouteFetcher.cs
@@ -3,7 +3,7 @@
using System.Linq;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Internal;
+//using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;
diff --git a/src/RouteJs.AspNet/GlobalSuppressions.cs b/src/RouteJs.AspNet/GlobalSuppressions.cs
new file mode 100644
index 0000000..9b548b4
--- /dev/null
+++ b/src/RouteJs.AspNet/GlobalSuppressions.cs
@@ -0,0 +1,8 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0063:Utiliser une instruction 'using' simple")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0037:Utiliser un nom de membre déduit", Scope = "member", Target = "~M:RouteJs.RouteJs.GetJsonData~System.String")]
diff --git a/src/RouteJs.AspNet/Properties/launchSettings.json b/src/RouteJs.AspNet/Properties/launchSettings.json
new file mode 100644
index 0000000..bd4b3a5
--- /dev/null
+++ b/src/RouteJs.AspNet/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:62667/",
+ "sslPort": 44316
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "RouteJs.AspNet": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RouteJs.AspNet/RouteJs.AspNet.csproj b/src/RouteJs.AspNet/RouteJs.AspNet.csproj
new file mode 100644
index 0000000..f67efe0
--- /dev/null
+++ b/src/RouteJs.AspNet/RouteJs.AspNet.csproj
@@ -0,0 +1,26 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
diff --git a/src/RouteJs.AspNet/RouteJsController.cs b/src/RouteJs.AspNet/RouteJsController.cs
index abb2803..db54f26 100644
--- a/src/RouteJs.AspNet/RouteJsController.cs
+++ b/src/RouteJs.AspNet/RouteJsController.cs
@@ -3,11 +3,12 @@
using Microsoft.AspNetCore.Mvc;
namespace RouteJs
-{
- ///
- /// ASP.NET MVC controller for RouteJs. Renders JavaScript to handle routing of ASP.NET URLs.
- ///
- public class RouteJsController : Controller
+{
+ ///
+ /// ASP.NET MVC controller for RouteJs. Renders JavaScript to handle routing of ASP.NET URLs.
+ ///
+ [ApiExplorerSettings(IgnoreApi = true)]
+ public class RouteJsController : Controller
{
///
/// How long to cache the JavaScript output for. Only used when a unique hash is present in the URL.
diff --git a/src/RouteJs.AspNet/RouteJsHelper.cs b/src/RouteJs.AspNet/RouteJsHelper.cs
index f1993ae..0546c8a 100644
--- a/src/RouteJs.AspNet/RouteJsHelper.cs
+++ b/src/RouteJs.AspNet/RouteJsHelper.cs
@@ -7,7 +7,8 @@
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Html;
-
+using Microsoft.Extensions.Hosting;
+
namespace RouteJs
{
///
@@ -37,7 +38,7 @@ public class RouteJsHelper : IRouteJsHelper
///
/// Hosting environment
///
- private readonly IHostingEnvironment _env;
+ private readonly IWebHostEnvironment _env;
private readonly IActionContextAccessor _actionContextAccessor;
@@ -51,7 +52,7 @@ public class RouteJsHelper : IRouteJsHelper
public RouteJsHelper(
IUrlHelperFactory urlHelperFactory,
IServiceProvider serviceProvider,
- IHostingEnvironment env,
+ IWebHostEnvironment env,
IActionContextAccessor actionContextAccessor)
{
_urlHelperFactory = urlHelperFactory;
diff --git a/src/RouteJs.AspNet/TemplateRouteFetcher.cs b/src/RouteJs.AspNet/TemplateRouteFetcher.cs
index 0165882..1f71bed 100644
--- a/src/RouteJs.AspNet/TemplateRouteFetcher.cs
+++ b/src/RouteJs.AspNet/TemplateRouteFetcher.cs
@@ -37,15 +37,18 @@ public TemplateRouteFetcher(IConstraintsProcessor constraintsProcessor, IRouteTe
/// Processed route information
public IEnumerable GetRoutes(RouteData routeData)
{
- var routeCollection = routeData.Routers.OfType().First();
- for (var i = 0; i < routeCollection.Count; i++)
- {
- var route = routeCollection[i];
- if (route is Route)
- {
- yield return ProcessTemplateRoute((Route)route);
- }
- }
+ var routeCollection = routeData.Routers.OfType().FirstOrDefault();
+ if (routeCollection != null)
+ {
+ for (var i = 0; i < routeCollection.Count; i++)
+ {
+ var route = routeCollection[i];
+ if (route is Route)
+ {
+ yield return ProcessTemplateRoute((Route)route);
+ }
+ }
+ }
}
///
diff --git a/src/RouteJs.AspNet/bundleconfig.json b/src/RouteJs.AspNet/bundleconfig.json
new file mode 100644
index 0000000..5436586
--- /dev/null
+++ b/src/RouteJs.AspNet/bundleconfig.json
@@ -0,0 +1,8 @@
+[
+ {
+ "outputFileName": "compiler/resources/router.min.js",
+ "inputFiles": [
+ "compiler/resources/router.js"
+ ]
+ }
+]
diff --git a/src/RouteJs.AspNet/compiler/resources/router.js b/src/RouteJs.AspNet/compiler/resources/router.js
new file mode 100644
index 0000000..a6602fb
--- /dev/null
+++ b/src/RouteJs.AspNet/compiler/resources/router.js
@@ -0,0 +1,324 @@
+/*!
+ * RouteJs by Daniel Lo Nigro (Daniel15) - http://dl.vc/routejs
+ * Version {VERSION}
+ * Released under the BSD license.
+ */
+(function (window) {
+ 'use strict';
+
+ // Helper methods
+ function merge (first, second) {
+ ///
+ /// Return a new object that contains the properties from both of these objects. If a property
+ /// exists in both objects, the property in `second` will override the property in `first`.
+ ///
+ var result = {},
+ key;
+
+ for (key in first) {
+ if (first.hasOwnProperty(key)) {
+ result[key] = first[key];
+ }
+ }
+
+ for (key in second) {
+ if (second.hasOwnProperty(key)) {
+ result[key] = second[key];
+ }
+ }
+
+ return result;
+ }
+
+ var arrayIndexOf;
+
+ // Check for native Array.indexOf support
+ if (Array.prototype.indexOf) {
+ arrayIndexOf = function (array, searchElement) {
+ return array.indexOf(searchElement);
+ };
+ } else {
+ arrayIndexOf = function (array, searchElement) {
+ for (var i = 0, count = array.length; i < count; i++) {
+ if (array[i] === searchElement) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ }
+
+ function escapeRegExp(string) {
+ ///
+ /// Escapes a string for usage in a regular expression
+ ///
+ /// Input string
+ /// String suitable for inserting in a regex
+
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ var Route = function (route, settings) {
+ ///Handles route processing
+ ///Route information
+
+ var paramRegex = /\{(\w+)\}/g,
+ matches;
+
+ if (!route.optional) {
+ route.optional = [];
+ }
+
+ this.settings = merge({ lowerCaseUrls: false }, settings);
+ this.route = route;
+ this._params = [];
+
+ // Grab all the parameters from the URL
+ while ((matches = paramRegex.exec(this.route.url)) !== null) {
+ this._params.push(matches[1].toLowerCase());
+ }
+ };
+
+ Route.prototype = {
+ build: function (routeValues) {
+ ///
+ /// Build a URL using this route, based on the passed route values. Returns null if the
+ /// route values provided are not sufficent to build a URL using this route.
+ ///
+ ///Route values
+ ///URL, or null when building a URL is not possible
+
+ // Keys of values are case insensitive and are converted to lowercase server-side.
+ // Convert keys of input to lowercase too.
+ var routeValuesLowercase = {};
+ for (var key in routeValues) {
+ if (routeValues.hasOwnProperty(key)) {
+ routeValuesLowercase[key.toLowerCase()] = routeValues[key];
+ }
+ }
+
+ var finalValues = merge(this.route.defaults, routeValuesLowercase),
+ processedParams = { controller: true, action: true },
+ finalUrl;
+
+ // Ensure area matches, if provided
+ if (
+ this.route.defaults.area &&
+ this.route.defaults.area.toLowerCase() !== (routeValuesLowercase.area || '').toLowerCase()
+ ) {
+ return null;
+ }
+
+ if (!this._checkConstraints(finalValues) || !this._checkNonDefaultValues(finalValues, processedParams)) {
+ return null;
+ }
+
+ // Try to merge all URL parameters
+ // If null, this means a required parameters was not specified.
+ finalUrl = this._merge(finalValues, processedParams);
+ if (finalUrl === null) {
+ return null;
+ }
+
+ finalUrl = this._trimOptional(finalUrl) + this._extraParams(routeValues, processedParams, finalUrl.indexOf('?') > -1);
+ return finalUrl;
+ },
+
+ _checkNonDefaultValues: function (finalValues, processedParams) {
+ ///Checks that any values using a non-default value have a matching merge field in the URL.
+ ///Route values merged with defaults
+ ///Array of parameters that have already been processed
+ ///true if all non-default parameters have a matching merge field, otherwise false.
+
+ for (var key in this.route.defaults) {
+ if (!this.route.defaults.hasOwnProperty(key)) {
+ continue;
+ }
+ // We don't care about case when comparing defaults.
+ if (
+ (this.route.defaults[key] + '').toLowerCase() !== (finalValues[key] + '').toLowerCase() &&
+ arrayIndexOf(this._params, key) === -1
+ ) {
+ return false;
+ } else {
+ // Any defaults don't need to be explicitly specified in the querystring
+ processedParams[key] = true;
+ }
+ }
+
+ return true;
+ },
+
+ _merge: function (finalValues, processedParams) {
+ ///
+ /// Merges parameters into the URL, keeping track of which parameters have been added and
+ /// ensuring that all required parameters are specified.
+ ///
+ ///Route values merged with defaults
+ ///Array of parameters that have already been processed
+ ///URL with parameters merged in, or null if not all parameters were specified
+
+ var finalUrl = this.settings.lowerCaseUrls ? this.route.url.toLowerCase() : this.route.url;
+
+ for (var i = 0, count = this._params.length; i < count; i++) {
+ var paramName = this._params[i],
+ isProvided = finalValues[paramName] !== undefined,
+ isOptional = arrayIndexOf(this.route.optional, paramName) > -1;
+
+ if (!isProvided && !isOptional) {
+ return null;
+ }
+
+ if (isProvided) {
+ var paramRegex = new RegExp('\{' + escapeRegExp(paramName) + '}', 'i');
+ var paramValue = this.settings.lowerCaseUrls && this._shouldConvertParam(paramName)
+ ? finalValues[paramName].toLowerCase()
+ : finalValues[paramName];
+ finalUrl = finalUrl.replace(paramRegex, encodeURIComponent(paramValue));
+ }
+
+ processedParams[paramName] = true;
+ }
+
+ return finalUrl;
+ },
+
+ _trimOptional: function (finalUrl) {
+ ///Trims any unused optional parameter segments from the end of the URL
+ ///URL with used parameters merged in
+ ///URL with unused optional parameters removed
+ var urlPieces = finalUrl.split('/');
+ for (var i = urlPieces.length - 1; i >= 0; i--) {
+ // If it has a parameter, assume it's an ignored one (otherwise it would have been merged above)
+ if (urlPieces[i].indexOf('{') > -1) {
+ urlPieces.splice(i, 1);
+ }
+ }
+ return urlPieces.join('/');
+ },
+
+ _extraParams: function (routeValues, processedParams, alreadyHasParams) {
+ ///Add any additional parameters not specified in the URL as querystring parameters
+ ///Route values
+ ///Array of parameters that have already been processed
+ ///Whether this URL already has querystring parameters in it
+ ///URL encoded querystring parameters
+
+ var params = '';
+
+ // Add all other parameters to the querystring
+ for (var key in routeValues) {
+ if (!processedParams[key.toLowerCase()]) {
+ params += (alreadyHasParams ? '&' : '?') + encodeURIComponent(key) + '=' + encodeURIComponent(routeValues[key]);
+ alreadyHasParams = true;
+ }
+ }
+
+ return params;
+ },
+
+ _checkConstraints: function (routeValues) {
+ ///Validate that the route constraints match the specified route values
+ ///Route values
+ ///true if the route validation succeeds, otherwise false.
+
+ // Bail out early if there's no constraints on this route
+ if (!this.route.constraints) {
+ return true;
+ }
+
+ if (!this._parsedConstraints) {
+ this._parsedConstraints = this._parseConstraints();
+ }
+
+ // Check every constraint matches
+ for (var key in this._parsedConstraints) {
+ if (this._parsedConstraints.hasOwnProperty(key) && !this._parsedConstraints[key].test(routeValues[key])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ _parseConstraints: function () {
+ ///Parse the string constraints into regular expressions
+
+ var parsedConstraints = {};
+
+ for (var key in this.route.constraints) {
+ if (this.route.constraints.hasOwnProperty(key)) {
+ parsedConstraints[key.toLowerCase()] =
+ new RegExp('^(' + this.route.constraints[key].replace(/\\/g, '\\') + ')$', 'i');
+ }
+ }
+
+ return parsedConstraints;
+ },
+
+ _shouldConvertParam: function (param) {
+ ///Gets if we should convert this param.
+ ///The param to check
+
+ return (
+ param === 'controller' ||
+ param === 'action' ||
+ param === 'area'
+ );
+ },
+ };
+
+ var RouteManager = function (settings) {
+ ///Manages routes and selecting the correct route to use when routing URLs
+ ///Raw route information
+
+ this.baseUrl = settings.baseUrl;
+ this.lowerCaseUrls = settings.lowerCaseUrls;
+ this.routes = [];
+ var routeSettings = {
+ lowerCaseUrls: this.lowerCaseUrls
+ };
+ for (var i = 0, count = settings.routes.length; i < count; i++) {
+ this.routes.push(new Route(settings.routes[i], routeSettings));
+ }
+ };
+
+ RouteManager.prototype = {
+ action: function (controller, action, routeValues) {
+ ///Generate a URL to an action
+ ///Name of the controller
+ ///Name of the action
+ ///Route values
+ ///URL for the specified action
+
+ routeValues = routeValues || { };
+ routeValues.controller = controller || window.RouteJs.activeController;
+ routeValues.action = action;
+ return this.route(routeValues);
+ },
+
+ route: function (routeValues) {
+ ///Generate a URL to an action
+ ///Route values
+ ///URL for the specified action
+
+ for (var i = 0, count = this.routes.length; i < count; i++) {
+ var url = this.routes[i].build(routeValues);
+ if (url) {
+ return this.baseUrl + url;
+ }
+ }
+
+ throw new Error('No route could be matched to route values: ' + routeValues);
+ }
+ };
+
+ // Public API
+ window.RouteJs = {
+ version: '{VERSION}',
+ Route: Route,
+ RouteManager: RouteManager
+ };
+}(window));
diff --git a/src/RouteJs.AspNet/compiler/resources/router.min.js b/src/RouteJs.AspNet/compiler/resources/router.min.js
new file mode 100644
index 0000000..71b8509
--- /dev/null
+++ b/src/RouteJs.AspNet/compiler/resources/router.min.js
@@ -0,0 +1,6 @@
+/*!
+ * RouteJs by Daniel Lo Nigro (Daniel15) - http://dl.vc/routejs
+ * Version {VERSION}
+ * Released under the BSD license.
+ */
+(function(n){"use strict";function u(n,t){var r={};for(var i in n)n.hasOwnProperty(i)&&(r[i]=n[i]);for(i in t)t.hasOwnProperty(i)&&(r[i]=t[i]);return r}function f(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var i,t,r;i=Array.prototype.indexOf?function(n,t){return n.indexOf(t)}:function(n,t){for(var i=0,r=n.length;i-1)},_checkNonDefaultValues:function(n,t){for(var r in this.route.defaults)if(this.route.defaults.hasOwnProperty(r)){if((this.route.defaults[r]+"").toLowerCase()!==(n[r]+"").toLowerCase()&&i(this._params,r)===-1)return!1;t[r]=!0}return!0},_merge:function(n,t){for(var s,h,u=this.settings.lowerCaseUrls?this.route.url.toLowerCase():this.route.url,e=0,c=this._params.length;e-1;if(!o&&!l)return null;o&&(s=new RegExp("{"+f(r)+"}","i"),h=this.settings.lowerCaseUrls&&this._shouldConvertParam(r)?n[r].toLowerCase():n[r],u=u.replace(s,encodeURIComponent(h)));t[r]=!0}return u},_trimOptional:function(n){for(var t=n.split("/"),i=t.length-1;i>=0;i--)t[i].indexOf("{")>-1&&t.splice(i,1);return t.join("/")},_extraParams:function(n,t,i){var u="";for(var r in n)t[r.toLowerCase()]||(u+=(i?"&":"?")+encodeURIComponent(r)+"="+encodeURIComponent(n[r]),i=!0);return u},_checkConstraints:function(n){if(!this.route.constraints)return!0;this._parsedConstraints||(this._parsedConstraints=this._parseConstraints());for(var t in this._parsedConstraints)if(this._parsedConstraints.hasOwnProperty(t)&&!this._parsedConstraints[t].test(n[t]))return!1;return!0},_parseConstraints:function(){var t={};for(var n in this.route.constraints)this.route.constraints.hasOwnProperty(n)&&(t[n.toLowerCase()]=new RegExp("^("+this.route.constraints[n].replace(/\\/g,"\\")+")$","i"));return t},_shouldConvertParam:function(n){return n==="controller"||n==="action"||n==="area"}};r=function(n){var r,i,u;for(this.baseUrl=n.baseUrl,this.lowerCaseUrls=n.lowerCaseUrls,this.routes=[],r={lowerCaseUrls:this.lowerCaseUrls},i=0,u=n.routes.length;i