【.Net core】ajax+.Net core Web API 场景下的请求参数传递示例

写在前面

在文档开始前需要明确的是两个问题:
1、http协议下的 Get方式可不可以携带body参数?
2、jquery ajax发起请求使用get方式,可不可以携带body参数?
在面试题中最常见的,就是get请求和post请求有什么不同,回答一般是,get通过url传递参数,post通过body传递参数。get传参大小有限制,post无限制。但实际上,Http协议中 Get是可以通过body携带参数的。具体自行百度,文中不做赘述。
至于为什么我们使用ajax发起get请求时,并不能将参数放到body中。原因是ajax是基于XMLHttpRequest封装的请求插件。而XmlHttpRequest的官方规范中声明了,当方法为get head 时。会将请求body置为null,如下图
image.png
我们知道ajax post参数是放到data中,而get请求中的data参数ajax是如何处理的呢?源码如下:我们可以看到先判断当前请求是否存在content也就是通过判断当前请求的方法来判断当前是否存在请求正文,然后会将data拼接到url后,再删除data属性
image.png


image.png
所以我们可以得出,两点结论:
1、Http协议中Get请求可以通过body传递参数
2、jquery ajax 并不支持get通过body参数,会将data编入url中
此外,我们都知道ajax 默认请求方式是get ,默认内容类型contentType 是:contentType: “application/x-www-form-urlencoded; charset=UTF-8”,而这种内容类型会将将请求参数以key1=value1&key2=value2这种键值对的方式进行组织,并放入到请求体中。
在明确这两点的基础上,再来看使用ajax请求webApi时如何传参

1、Get方式请求

1、GET-基础类型参数

正确后端代码

/// <summary>
/// 基础类型做参数
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[HttpGet]
[Route("GetParam")]
public string GetParam(string param,int param1,DateTime param2)
{
	return param,param1,param2;
}

正确前端代码示例

// GET请求使用data传递参数,实际上ajax会将data内键值对追加到url上
// 需要注意,这里 contentType不要指定"application/json",因为参数都是通过url传递的
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetParam",
  data: {param: 1,param1:2,param2:new Date().toDateString()},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 或url拼接
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetParam?param=1&param1=2&param2=2023-02-09",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

GIF 2023-2-9 11-15-51.gif

2、GET-实体类型参数(不推荐)

首先阐明不推荐这样传参的原因:
Get请求长度限制问题。
此处简单介绍get post 参数长度问题,要明确的一点是,HTTP协议对于GET \POST 传参并无任何长度限制。那么为什么面试题中get和post 的不同点都会提到,get方式传参,长度有限制?这是因为针对get请求,浏览器层面进行了参数的长度限制,确切的说是url长度限制。因为当我们使用ajax时,即使通过data传递参数,ajax还是会将data中的参数追加到请求的url中去。并且不同的浏览器get请求url长度限制不同。
同样的,对于post请求的参数大小限制,HTTP协议并无要求,但不同的服务器,不同的后台框架,都会有默认的大小限制。但是这个限制要远比浏览器层面对于get请求长度限制大得多。
所以当我们传递复杂类型参数时,如果我们能够预估到参数会很大,就尽量选择post方式传递参数,从而避免浏览器层面对get请求进行url长度限制。

错误后端代码示例

/// <summary>
/// 实体类型做参数
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[HttpGet]
[Route("GetEntity")]
public string GetEntity(ParamEntity entity)
{
	return entity.Param1 + entity.Param2 +entity.Param2;
}

错误前端代码示例

// 通过data传递,我们知道ajax会将data中参数自动以&形式追加到请求url中,所以此方式等同与下面
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetParam",
  data: {param: 1,param1:2,param2:new Date().toDateString()},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 或url拼接
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetParam?param=1&param1=2&param2=2023-02-09",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 或contenetType = application/json + url拼接
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntity",
  contentType: "application/json",
  data: {param: 1,param1:2,param2:'2023-02-09'},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

错误调试代码截图

当使用ajax 默认的contentType 时,会发现错误代码415,也就是咱们的contentType不对
GIF 2023-2-9 16-31-00.gif
将contentType配置为application/json时,请求结果如下
image.png
错误代码为400,为参数类型不匹配。

综上可以看出,当WebApi 的get请求参数为实体类时,前端ajax 并不能进行正确请求。这个时候就我们可以通过[FromUri] 和[FromQuery] [FromBody]绑定源特性,来获取实体类型参数。
1、[FromUri] 是.net Framework版本 中提供的一个绑定源特性,
2、[FromQuery]是net core 提供。与前者作用相同,都是从url?后的查询字符串中,进行模型绑定。下文中以FromQuery 进行演示。

FromQuery特性获取实体参数

正确后端代码示例
/// <summary>
/// FromQuery 接受实体类型做参数
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[HttpGet]
[Route("GetEntity")]
public string GetEntity([FromQuery]ParamEntity entity)
{
	return entity.Param1 + entity.Param2 +entity.Param2;
}
正确前端代码示例

$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntity",
  data: {param: 1,param1:2,param2:'2023-02-09'},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

// 或contentType:'application/json' 依然可以
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntity",
  contentType:'application/json',
  data: {param: 1,param1:2,param2:'2023-02-09'},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
调试代码截图

image.png

3、GET-数组类型参数(不推荐)

我们知道,string[] arrays 这种数据类型当然不属于基础类型,所以我们直接使用FromQuery 方式来接收数组参数。

正确后端代码示例

/// <summary>
/// FromQuery 接收数组类型
/// </summary>
/// <param name="arrays"></param>
/// <returns></returns>
[HttpGet]
[Route("GetArrays")]
public string GetEntity([FromQuery]string[] arrays)
{
    return arrays.ToString();
}

错误前端代码示例

// 后端参数名称arrays ,请求实际url为 http://localhost:5000/TestGet/GetArrays?arrays%5B%5D=1&arrays%5B%5D=2&arrays%5B%5D=3
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays",
  data: {arrays:[1,2,3]},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

// 直接传递数组,请求实际url为 http://localhost:5000/TestGet/GetArrays?undefined=&undefined=&undefined=
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays",
  data: [1,2,3],
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

// 我们知道FromQuery 是从url中获取值,然后绑定到对应的model中,观察这两种方式的实际url,发现名称都不是arrays 

正确前端代码示例

// 1
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays?arrays=1&arrays=2",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 2
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays?arrays[0]=1&arrays[1]=2",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 3
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays?[0]=1&[1]=2",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 4
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays?arrays[a]=1&arrays[b]=2&arrays.index=a&arrays.index=b",
  // contentType:'application/json',
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 5
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetArrays?[a]=1&[b]=2&index=a&index=b",
  // contentType:'application/json',
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

4、GET-实体List参数(不推荐)

正确后端代码示例

/// <summary>
/// FromQuery 接收实体List类型
/// </summary>
/// <param name="arrays"></param>
/// <returns></returns>
[HttpGet]
[Route("GetEntityList")]
public string GetEntityList([FromQuery]List<ParamEntity> entities)
{
    return entities.Count.ToString();
}

正确前端代码示例

// 1
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntityList?entities[0].param=1&entities[0].param1=2&entities[0].param2=2023-02-09",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 2
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntityList?[0].param=1&[0].param1=2&[0].param2=2023-02-09",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 3
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntityList?entities[a].param=1&entities[a].param1=2&entities.index=a",
  // contentType:'application/json',
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
// 4
$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetEntityList?[a].param=1&[a].param1=2&index=a",
  // contentType:'application/json',
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

5、GET-Dictionary参数(不推荐)

正确后端代码示例

/// <summary>
/// FromQuery Dictionary参数
/// </summary>
/// <param name="dict"></param>
/// <returns></returns>
[HttpGet]
[Route("GetDictionary")]
public string GetDictionary([FromQuery] Dictionary<string,string> dict)
{
    return dict.ToString();
}

正确前端代码示例

$.ajax({
  type: "get",
  url: "http://localhost:5000/TestGet/GetDictionary",
  data: {param:1,param1:2,param2:'2023-02-09'},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

2、Post方式请求

如果你使用的版本是net core 3.0 ,请手动安装Microsoft.AspNetCore.Mvc.NewtonsoftJson,注意不是NewtonsoftJson包,因为从ASP.NET Core 3.0开始,默认使用了微软内置的JSON序列化和反序列化类库:System.Text.Json,此类库中并没有像Json.NET中实现这些特殊数据类型的JSON转换,在使用复杂类型接收参数时,会出现转换错误。
安装
Microsoft.AspNetCore.Mvc.NewtonsoftJson后,

在stratup.cs 中services.AddControllers().AddNewtonsoftJson();

1、POST-基础类型参数

1、通过在post请求的url中追加查询字符串
正确后端代码
/// <summary>
/// FromQuery Dictionary参数
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[HttpPost]
[Route("PostString")]
public string PostString(string param,string param1)
{
	return param1+param1;
}

错误前端代码示例
$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostString",
  data: {"param":1,"param1":2},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
正确前端代码示例
$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostString?param=1&param1=2",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
调试代码截图

image.png

2、通过在post请求的body中传递参数(不推荐!!)
错误后端代码
// 错误是因为FormBody在一个action中仅允许出现一次,因为请求主体在流中,而流只能打开一次
// 这样会导致编译失败
[HttpPost]
[Route("PostString")]
public string PostString([FromBody]string param,[FromBody]string param1)
{
	return param1+param1;
}

正确后端代码
/// <summary>
///  string参数
/// </summary>
/// <param name="dict"></param>
/// <returns></returns>
[HttpPost]
[Route("PostString")]
public string PostString([FromBody]string param,[FromQuery] string param1)
{
	return param.ToString();
}

错误前端代码示例
// 错误原因是因为ajax 的body 是 =1,参数绑定时会报400错
$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostString?param1=2",
  contentType: 'application/json',
  data: {"":"1"},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

// 错误原因是 这样传递,前台传递的是int类型,后台int 与 string不匹配,导致400报错
$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostString?param1=2",
  contentType: 'application/json',
  data: "1",
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
正确前端代码示例
// 这里必须与后端的参数的类型对应,若string ,参数就要手动加上双引号,如果直接 data:'1' 或data:1
// 会导致400参数类型错误
$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostString?param1=2",
  contentType: 'application/json',
  data: '"'+1+'"',
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});
调试代码截图

image.png

2、POST-实体类型参数

正确后端代码

/// <summary>
/// Post 接收 entity参数
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
[Route("PostEntity")]
public string PostEntity(ParamEntity entity)
{
	return entity.Param;
}

错误前端代码示例

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostEntity",
  contentType:'application/json',
  data: {Param: 1,Param1:2,Param2:'2023-02-09'},
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

正确前端代码示例

必须要将data使用JSON.stringify()包一下

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostEntity",
  contentType:'application/json',
  data: JSON.stringify({Param: 1,Param1:2,Param2:'2023-02-09'}),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

3、POST-数组类型参数

正确后端代码

/// <summary>
/// Post 接收 entity参数
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
[Route("PostEntity")]
public string PostEntity(ParamEntity entity)
{
	return entity.Param;
}

正确前端代码示例

必须要将data使用JSON.stringify()包一下

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostArrays",
  contentType:'application/json',
  data: JSON.stringify([1,2,3,4]),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

4、POST-实体List参数

正确后端代码示例

/// <summary>
///  Post接收实体类集合
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Route("PostEntityList")]
public string PostEntityList(List<ParamEntity> entities)
{
    return entities.Count.ToString();
}

正确前端代码示例


$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostEntityList",
  contentType:'application/json',
  data: JSON.stringify([{Param: 1,Param1:2,Param2:'2023-02-09'},{Param: 1,Param1:2,Param2:'2023-02-09'}]),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

5、POST-动态类型参数

当我们不想new 一个临时类做参数时,或参数属性是动态时,我们可以使用dynamic类做参数接收类型,但不推荐使用object类做参数类型。因为只要你使用object,就意味着你必须要进行类型强制转换,这就增加了系统异常的风险,使用object还不如使用字符串,然后后端字符串反序列化成对象。当我们使用Post接收基础数据类型时,完全可以使用dynamic来接收,因为基础数据类型没有属性的不确定性,而且避免了将参数放到url中。

正确后端代码示例

/// <summary>
///  Post接收动态类型参数
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[Route("PostDynamic")]
public string PostDynamic(dynamic obj)
{
    return obj.Param1;
}

正确前端代码示例

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostDynamic",
  contentType:'application/json',
  data: JSON.stringify({Param: 1,Param1:2,Param2:'2023-02-09'}),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

6、POST-Dictionary类型参数(value为基础数据类型)

正确后端代码示例

/// <summary>
///  Post接收Dictionary类型参数
/// </summary>
/// <param name="dictionary"></param>
/// <returns></returns>
[Route("PostDictionary")]
public string PostDictionary(Dictionary<string,string> dictionary)
{
    return dictionary.Keys.ToString();
}

正确前端代码示例

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostDictionary",
  contentType: 'application/json',
  data: JSON.stringify({param: 1, param1: 2, param2: '2023-02-09'}),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

7、POST-Dictionary类型参数(value为实体数据类型)

正确后端代码示例

/// <summary>
///  Post接收Dictionary value为实体类型参数
/// </summary>
/// <param name="dictionary"></param>
/// <returns></returns>
[Route("PostDictionaryValueEntity")]
public string PostDictionaryValueEntity(Dictionary<string,ParamEntity> dictionary)
{
    return dictionary.Keys.ToString();
}

正确前端代码示例

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/PostDictionaryValueEntity",
  contentType: 'application/json',
  data: JSON.stringify({
    param: {param: 1, param1: 2, param2: '2023-02-09'},
    param1: {param: 1, param1: 2, param2: '2023-02-09'},
    param2: {param: 1, param1: 2, param2: '2023-02-09'}
  }),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

调试代码截图

image.png

3、绑定源特性

以net core 3.0 为例,存在以下绑定源特性:
image.png

微软官方文档:https://learn.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-7.0&viewFallbackFrom=aspnetcore-3

特性绑定源
[FromBody]请求正文
[FromForm]请求正文中的表单数据
[FromHeader]请求标头
[FromQuery]请求查询字符串参数
[FromRoute]当前请求中的路由数据

那么到底什么是绑定源特性?按照微软官方文档解释来理解,感觉晦涩难懂。个人理解为从请求的不同位置获取源数据,并且通过不同的绑定方式将数据绑定到使用了[FromBody]这种特性的参数上。

1、[FromBody]用法

1、post请求
2、基础数据类型参数,且必须要在post请求的body中传递。其他场景中不推荐使用。因为以post 传递实体类型参数为例,即使不加此标记,依然可以实现自动绑定

/// <summary>
///  FromBody FromQuery
/// </summary>
/// <param name="param"></param>
/// <param name="param1"></param>
/// <returns></returns>
[HttpPost]
[Route("PostString")]
public string PostString([FromBody] string param, [FromQuery] string param1)
{
    return param.ToString();
}

2、[FromForm]用法

1、使用原始表单提交
2、ajax 指定contentType: application/x-www-form-urlencoded

<form action="http://localhost:5000/TestPost/PostEntity" method="post" id="test_form">
  <input type="text" name="param" value="" />
  <input type="text" name="param1" value="" />
</form>
<button onclick="submit()">提交</button>

function submit(val){
document.getElementById('test_form').submit();
return false
}

/// <summary>
///  FromForm
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Route("PostEntity")]
public string PostEntity([FromForm] ParamEntity entity)
{
return entity.Param;
}

3、[FromQuery]用法

1、从查询字符串中获取参数,自动绑定
2、常用于多个参数的请求,用于区分哪些参数需要从url中绑定

/// <summary>
///  FromBody FromQuery
/// </summary>
/// <param name="param"></param>
/// <param name="param1"></param>
/// <returns></returns>
[HttpPost]
[Route("PostString")]
public string PostString([FromBody] string param, [FromQuery] string param1)
{
    return param.ToString();
}

4、[FromRoute]用法

从路由,实际上也是从url,中获取参数,自动绑定

// 1、可以通过Route中指定
/// <summary>
///  TestRoute
/// </summary>
/// <param name="param"></param>
/// <param name="param1"></param>
/// <param name="param2"></param>
/// <returns></returns>
[Route("TestRoute/{param}/{param1}/{param2}")]
public string TestRoute(string param, string param1,string param2)
{
    return param;
}

// 2、也可以通过实体类接收,在实体类中指定属性的绑定源
/// <summary>
///  TestRoute
/// </summary>
/// <param name="param"></param>
/// <param name="param1"></param>
/// <param name="param2"></param>
/// <returns></returns>
// [Route("TestRoute/{param}/{param1}/{param2}")]
[Route("{param}/{param1}/{param2}/TestRoute")]
public string TestRoute([FromQuery]RouteEntity entity)
{
    return entity.Param;
}

public class RouteEntity
{
    [FromRoute(Name = "param")]
    public string Param { get; set; }
    [FromRoute(Name = "param1")]
    public string Param1 { get; set; }
    [FromRoute(Name = "param2")]
    public string Param2 { get; set; }
    [FromQuery]
    public string Param3 { get; set; }
}
$.ajax({
    type: "post",
    url: "http://localhost:5000/TestPost/1/2/3/TestRoute?param3=4",
    contentType: 'application/json',
    success: function (data) {
        layer.msg(JSON.stringify(data));
    }
});

image.png

5、[FromHeader]用法

从Header中获取参数,自动绑定

/// <summary>
///  TestHeader
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[Route("TestHeader")]
public string TestHeader([FromHeader]string param)
{
    return param;
}
$.ajax({
  headers:{
    "param": "1"
  },
  type: "post",
  url: "http://localhost:5000/TestPost/TestHeader",
  contentType: 'application/json',
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

image.png

4、WebApi 参数内置校验特性及自定义

对于参数校验,我们一般的方式如下:

/// <summary>
///  TestValidate
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Route("TestValidate")]
public string TestValidate(ParamEntity entity)
{
    if (string.IsNullOrEmpty(entity.Param))
    {
        return "entity.param should not be null or empty";
    }

    return entity.Param;
}

但实际上,c# WebApi提供了很多内置的校验特性,继承自
System.ComponentModel.DataAnnotations.ValidationAttributede的一些特性如下:
image.png
其中System.ComponentModel.DataAnnotations.DataTypeAttribute 还有不同的子类,如下:
image.png

1、使用c# 内置校验特性实现接口参数校验

后端代码

相比与传统的校验方式,我们可以发现,通过校验特性来进行参数校验,大大减少了代码冗余,将复杂且冗余的逻辑判断,使用特性代替,代码如下:

/// <summary>
///  TestValidate
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[Route("TestValidate")]
public string TestValidate(ParamEntity entity)
{
    return entity.Param;
}

public class ParamEntity
{
    [Required(ErrorMessage = "这个参数是必须的!")]
    [MaxLength(length:20,ErrorMessage = "这个参数最大长度不得超过20!")]
    public string Param { get; set; }
    /// <summary>
    /// 3位数字
    /// </summary>
    [RegularExpression(pattern:"^\\d{3}$",ErrorMessage = "必须是3位数字")]
    public string Param1 { get; set; }
    public string Param2 { get; set; }
}

前端代码

$.ajax({
  type: "post",
  url: "http://localhost:5000/TestPost/TestValidate",
  contentType:'application/json',
  data: JSON.stringify({Param: "1",Param1:"22222",Param2:'2023-02-09'}),
  success: function (data) {
    layer.msg(JSON.stringify(data));
  }
});

这个时候,请求就不会进入后台接口,直接返回400参数类型错误,截图如下:
image.png

image.png

2、使用自定义校验特性实现接口参数校验

c# 内置的校验特性都是继承自System.ComponentModel.DataAnnotations.ValidationAttributede类,那么我们要编写自己的校验特性,首先需要继承这个类,然后在自己的类中重写校验方法即可。代码如下,以校验EmpNO为例:

后端代码

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
        AllowMultiple = false)]
    public class EmpNoValidateAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var empNo = value.ToString();
            return !empNo.StartsWith("E")
                ? new ValidationResult("EmpNo必须要以E开头")
                : base.IsValid(value, validationContext);
        }
    }

使用方式如下:
image.png

3、对于参数校验400错误,统一返回格式

为什么需要统一的返回格式,在网上找的文章如下:
image.png

1、编写自己的过滤器

/// <summary>
/// 参数校验过滤器
/// 返回统一格式消息
/// </summary>
public class ValidateReqModelFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // 在响应体中返回验证错误信息
            var errors = new Dictionary<string, IEnumerable<string>>();
            foreach (KeyValuePair<string, ModelStateEntry> keyValue in context.ModelState)
            {
                errors[keyValue.Key] = keyValue.Value.Errors.Select(e => e.ErrorMessage);
            }
            context.Result = new JsonResult(new { code = -1, errmsg = errors });
        }
    }
}

2、禁用c#自带的参数验证过滤器

如果不禁用自带过滤器,则会直接返回400,导致我们自己定义的过滤器虽然注册,但是不会执行

services.Configure<ApiBehaviorOptions>(options => options.SuppressModelStateInvalidFilter = true);

3、注册自己的过滤器

红色代码部分:
services.AddControllers(options => options.Filters.Add(typeof(ValidateReqModelFilter))).AddNewtonsoftJson();

4、接口返回截图

状态码:200
image.png
示例:
image.png

  • 58
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
ASP.NET Core 并不直接支持调用 WinForms 自定义控件,因为 ASP.NET Core 是基于跨平台的开发框架,而 WinForms 是一个基于 Windows 平台的桌面应用程序框架。不过,你可以通过一些技巧来实现在 ASP.NET Core 中使用 WinForms 控件。 一种常见的做法是使用 SignalR 实现实时通信,将 WinForms 控件的 UI 渲染在服务器端,并通过 SignalR 将渲染后的 UI 推送给客户端。这样,客户端就可以通过浏览器访问 ASP.NET Core 应用程序并查看 WinForms 控件的渲染结果。 下面是一个简单的示例,演示如何在 ASP.NET Core 中使用 WinForms 控件: 1. 创建一个 ASP.NET Core Web 应用程序项目。 2. 在项目中添加对 `Microsoft.AspNetCore.SignalR` 的引用。 3. 创建一个名为 `WinFormsService` 的类,用于处理 WinForms 控件的渲染和更新: ```csharp using System; using System.Drawing; using System.Windows.Forms; public class WinFormsService { private readonly HubContext<WinFormsHub> _hubContext; private readonly Control _winFormsControl; public WinFormsService(HubContext<WinFormsHub> hubContext) { _hubContext = hubContext; _winFormsControl = new YourCustomControl(); // 替换为你自己的自定义控件类型 _winFormsControl.Size = new Size(300, 300); _winFormsControl.Paint += WinFormsControl_Paint; } private void WinFormsControl_Paint(object sender, PaintEventArgs e) { // 在控件的绘制事件中渲染 UI,并将渲染结果发送给客户端 using (var bitmap = new Bitmap(_winFormsControl.Width, _winFormsControl.Height)) { _winFormsControl.DrawToBitmap(bitmap, _winFormsControl.ClientRectangle); var imageData = ImageToBase64(bitmap); _hubContext.Clients.All.SendAsync("UpdateWinFormsUI", imageData); } } private string ImageToBase64(Image image) { using (var memoryStream = new MemoryStream()) { image.Save(memoryStream, ImageFormat.Png); var imageDataBytes = memoryStream.ToArray(); return Convert.ToBase64String(imageDataBytes); } } } ``` 4. 创建一个名为 `WinFormsHub` 的 SignalR Hub 类,用于处理客户端的连接和消息: ```csharp using Microsoft.AspNetCore.SignalR; public class WinFormsHub : Hub { public async Task JoinGroup(string groupName) { await Groups.AddToGroupAsync(Context.ConnectionId, groupName); } public async Task LeaveGroup(string groupName) { await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName); } } ``` 5. 在 `Startup.cs` 文件的 `ConfigureServices` 方法中注册服务: ```csharp public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); services.AddSingleton<WinFormsService>(); } ``` 6. 在 `Startup.cs` 文件的 `Configure` 方法中启用 SignalR 终结点: ```csharp public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapHub<WinFormsHub>("/winformsHub"); // SignalR 终结点 endpoints.MapControllers(); }); } ``` 7. 创建一个名为 `WinFormsController` 的控制器,用于处理客户端的请求: ```csharp [Route("api/[controller]")] [ApiController] public class WinFormsController : ControllerBase { private readonly WinFormsService _winFormsService; public WinFormsController(WinFormsService winFormsService) { _winFormsService = winFormsService; } [HttpGet("start")] public IActionResult Start() { // 启动 WinForms 控件渲染 _winFormsService.StartRendering(); return Ok(); } } ``` 8. 在客户端的 HTML 页面中使用 SignalR 连接并接收来自服务器的渲染结果: ```html <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="/winformsHub/hubs"></script> <script> const connection = new signalR.HubConnectionBuilder() .withUrl("/winformsHub") .build(); connection.on("UpdateWinFormsUI", function (imageData) { // 接收来自服务器的渲染结果,并将其显示在页面上 const imageElement = document.getElementById("winformsImage"); imageElement.src = "data:image/png;base64," + imageData; }); connection.start().then(function () { connection.invoke("JoinGroup", "WinFormsGroup"); }); </script> <img id="winformsImage" src="" alt="WinForms UI"> ``` 以上示例中,`WinFormsService` 类负责渲染 WinForms 控件的 UI,并使用 SignalR 将渲染结果推送给客户端。`WinFormsHub` 类是 SignalR 的 Hub 类,用于处理客户端的连接和消息。`WinFormsController` 类是一个 ASP.NET Core 控制器,用于启动 WinForms 控件的渲染。 请注意,上述示例仅为演示目的,实际应用中可能需要根据具体需求进行修改和扩展。此外,这种方法可能会带来一些性能和安全方面的考虑,请根据实际情况进行评估和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值