在 .net core razor 中将 json 字符串作为路由参数传递

Pass json string as Route Parameter in .net core razor

我有一个将数组[]作为参数的操作方法,如下所示

public IActionResult MyAction(string[] data)
{
   -- Further Implementation -- 
   return View(model);
}

在视图方面,我使用以下代码,这里我使用路由将数据传递给控制器​​。

 var array = JSON.stringify(cityList); --This is Json String which i wanter to pass 

-- want convert this json String into c# string.  

 window.location.href = "@Url.RouteUrl("PrintWorkAssignement",**Need to pass string**)";

So is there any way that i can convert that json string into C# string, so that i can place it as a parameter and send it to controller ?

因为您使用 Url.RouteUrl 所以我假设您有一个按路线名称定位的操作。这也意味着您需要将路由值传递给 string[] 类型的绑定参数。所以在这种情况下,它是一个复杂的类型。该值不会为您自动转换。您需要一个输入(原始)值为 json 的自定义 IModelBinder,如下所示:

public class JsonEnumerableBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (typeof(IEnumerable).IsAssignableFrom(bindingContext.ModelType))
        {
            var value = bindingContext.ValueProvider.GetValue(bindingContext.FieldName).FirstValue;
            if (!string.IsNullOrWhiteSpace(value))
            {
                try
                {
                    var modelType = bindingContext.ModelType == typeof(IEnumerable) ? typeof(List<object>) :
                                    bindingContext.ModelType.IsGenericType &&
                                    bindingContext.ModelType.GetGenericTypeDefinition() == typeof(IEnumerable<>) ?
                                    typeof(List<object>).MakeGenericType(bindingContext.ModelType.GetGenericArguments()[0]) :
                                    bindingContext.ModelType;
                    bindingContext.Result = ModelBindingResult.Success(JsonSerializer.Deserialize(value, modelType));
                }
                catch
                {
                    bindingContext.Result = ModelBindingResult.Failed();
                }
            }
        }
        return Task.CompletedTask;
    }
}

请注意,上面的模型绑定器可以将值绑定到 IEnumerable 类型的参数,如 string[]List<T>、...所以它比 [=] 的参数类型更通用17=].

您可以使用 IModelBinderProvider 全局 提供您的自定义模型活页夹。但是在这种情况下,我建议在 ModelBinderAttribute 的帮助下使用显式参数范围绑定,如下所示:

public IActionResult MyAction([ModelBinder(typeof(JsonEnumerableBinder))] string[] data) { ... }

下面是您如何传递 Url.RouteUrl 的值:

//json is the json string you have
window.location.href = "@Url.RouteUrl("PrintWorkAssignement", new { data = json })";

最后有一条关于不好看的 URL 生成的注释,其中 JSON 是路径(段)的一部分。因为正如我所说,您似乎将 data 与路由模式中出现的路由值绑定,如下所示:

[Route("parent/{data}", Name = "PrintWorkAssignement")]
public IActionResult MyAction([ModelBinder(typeof(JsonEnumerableBinder))] string[] data) {...}

那当然会产生难看的 URLs。因此,在这种情况下,您可能希望使用查询字符串来绑定 data 的值。 json 附加在路径的末尾(作为查询字符串值)看起来更好。要使用查询字符串,只需删除路由模式中的 {data} 部分。该值将作为查询字符串附加,您的自定义 IModelBinder 仍然有效。但是,如果将其绑定为查询字符串,您还有另一种选择可以使其工作,而无需传递 json 并需要自定义 IModelBinder(正如我们在上面应用的那样)。 string[] 可以从一组查询字符串变量中绑定,每个查询字符串变量都是一个项目,就像这样 data[0]=abc&data[1]=xyz。这就是您查询字符串所需的格式(不是 json)(您不再需要自定义 IModelBinder)。首先,您需要一个 javascript 函数来将您的数组转换为该格式的值:

//just a simple function target an array of string
//you can build a more complex function (or find somewhere) which targets an object graph
function getQueryString(stringArray, paramName){
   return stringArray.map(function(e,i) { return paramName + "[" + i + "]=" + e;  })
                     .join("&");
}

请注意,在 jQuery 中你有函数 $.param 应该可以工作,但对于这种情况(格式化字符串数组),它会以某种方式转换为这样的格式 data[]=abc&data[]=xyz (缺少索引)。该格式不起作用,并且在服务器端不会为您绑定任何值(至少那是我在 asp.net core 2.2 上测试过的)。始终包含索引以确保其正常工作。

现在使用该函数,您可以像这样更改为使用查询字符串:

var queryString = getQueryString(yourStringArray, "data");

//your link built by query string, remember to remove the [ModelBinder(...)] on your action method
window.location.href = "@Url.RouteUrl("PrintWorkAssignement")?" + queryString;

作为查询参数的数组应如下所示:

www.test.com/api/testaction?data=first&data=second&data=third

注意值的名称应与操作方法中的参数名称相同(data)