GVKun编程网logo

是否允许实体正文进行HTTP DELETE请求?(是否允许从此web站点访问下列应用程序提示去掉)

6

在本文中,我们将为您详细介绍是否允许实体正文进行HTTPDELETE请求?的相关知识,并且为您解答关于是否允许从此web站点访问下列应用程序提示去掉的疑问,此外,我们还会提供一些关于.NET6开发To

在本文中,我们将为您详细介绍是否允许实体正文进行HTTP DELETE请求?的相关知识,并且为您解答关于是否允许从此web站点访问下列应用程序提示去掉的疑问,此外,我们还会提供一些关于.NET 6开发TodoList应用之实现DELETE请求与HTTP请求幂等性、ajax – 带有正文的Angular $http.delete请求、ajax 参数传递之 [HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete] 请求、ajax参数传递之[HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]请求的有用信息。

本文目录一览:

是否允许实体正文进行HTTP DELETE请求?(是否允许从此web站点访问下列应用程序提示去掉)

是否允许实体正文进行HTTP DELETE请求?(是否允许从此web站点访问下列应用程序提示去掉)

发出HTTP DELETE请求时,请求URI应该完全标识要删除的资源。但是,是否可以在请求的实体中添加额外的元数据?

答案1

小编典典

612

该规范没有明确禁止或阻止它,因此我倾向于说它是允许的。

微软认为它以同样的方式(我可以听到观众低声),他们MSDN文章关于国有ADO.NET数据服务框架的删除方法:

如果DELETE请求包含实体主体,则将忽略该主体[…]

另外,这是RFC2616(HTTP 1.1)关于请求必须说的内容:

仅当消息主体存在时,实体主体才存在(第7.2节)
通过包含或标头来表示消息正文的存在(第4.3节)Content-LengthTransfer-Encoding
一个消息体必须不被包括在所述请求的方法的规范不允许发送实体主体(第4.3节)
一个实体主体在TRACE明确禁止仅请求,所有其他请求类型不受限制(第9节和9.8特异性)
对于响应,已定义:

是否包含消息正文取决于请求方法和响应状态(第4.3节)
明确禁止在响应HEAD请求时使用消息正文(特别是第9和9.4节)
在1xx(信息性),204(无内容)和304(未修改)响应中明确禁止了消息正文(第4.3节)
所有其他响应都包括一个消息正文,尽管它的长度可能为零(第4.3节)

.NET 6开发TodoList应用之实现DELETE请求与HTTP请求幂等性

.NET 6开发TodoList应用之实现DELETE请求与HTTP请求幂等性

需求

先说明一下关于原本想要去更新的PATCH请求的文章,从目前试验的情况来看,如果是按照.NET 6的项目结构(即只使用一个Program.cs完成程序初始化),那微软官方给出的文档目前还没有对应地更新,按照之前的方式进行JsonPatch的配置是不行的,目前已经有人在Github微软的官方文档Repo下提了ISSUE: .NET 6: JsonPatch in ASP.NET Core web API。并且因为PATCH的使用频率并不高,所以我暂时跳过那篇,先把进度继续往后走,看微软什么时候把这个issue解决一下我再看情况把PATCH那一节补上。

本文我们来看最后一个常用HTTP请求类型:DELETE。

目标

实现并验证应用正确处理DELETE请求。并对HTTP请求的幂等性做简单的介绍。

原理与思路

经过关于Create、Update、Get的实现,对于Delete的实现我们的思路是很清晰的。我们需要创建Delete的Command及其Handler,然后在Controller中通过Mediatr发送请求即可。

实现

在Application/TodoList下新建DeleteTodoList文件夹,并新建DeleteTodoListCommand:

DeleteTodoListCommand.cs

using MediatR;
using TodoList.Application.Common.Exceptions;
using TodoList.Application.Common.Interfaces;

namespace TodoList.Application.TodoLists.Commands.DeleteTodoList;

public class DeleteTodoListCommand : IRequest
{
    public Guid Id { get; set; }
}

public class DeleteTodoListCommandHandler : IRequestHandler<DeleteTodoListCommand>
{
    private readonly IRepository<Domain.Entities.TodoList> _repository;

    public DeleteTodoListCommandHandler(IRepository<Domain.Entities.TodoList> repository)
    {
        _repository = repository;
    }

    public async Task<Unit> Handle(DeleteTodoListCommand request, CancellationToken cancellationToken)
    {
        var entity = await _repository.GetAsync(request.Id);
        if (entity == null)
        {
            throw new NotFoundException(nameof(TodoList), request.Id);
        }

        await _repository.DeleteAsync(entity,cancellationToken);
        // 对于Delete操作,演示中并不返回任何实际的对象,可以结合实际需要返回特定的对象。Unit对象在MediatR中表示Void
        return Unit.Value;
    }
}

在Controller中添加Delete的接口处理:

TodoListController.cs

// 省略其他...
[HttpDelete("{id:guid}")]
public async Task<ApiResponse<object>> Delete(Guid id)
{
    return ApiResponse<object>.Success(await _mediator.Send(new DeleteTodoListCommand { Id = id }));
}

这里可能值得强调的是关于EntityFrameworkCore中对于关联实体DELETE操作的处理方式:

打开Infrastructure/Migrations文件夹,我们可以在迁移领域实体的那次Migration生成的.Designer.cs文件中发现这样一段配置:

// 省略其他...
modelBuilder.Entity("TodoList.Domain.Entities.TodoItem", b =>
    {
        b.HasOne("TodoList.Domain.Entities.TodoList", "List")
            .WithMany("Items")
            .HasForeignKey("ListId")
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();
        b.Navigation("List");
    });

可以看到在OnDelete中配置的是DeleteBehavior.Cascade行为模式,关于DeleteBehavior,可以参考Referential Constraint Action Options。实际上总共有七种可以设置的行为模式:

  • DeleteBehavior.Cascade
  • DeleteBehavior.NoAction
  • DeleteBehavior.Restrict
  • DeleteBehavior.SetNull
  • DeleteBehavior.ClientCascade
  • DeleteBehavior.ClientNoAction
  • DeleteBehavior.ClientSetNull

关于这七种DeleteBehavior,可以参考这篇博文:Entity Framework Core 关联删除,博主在其中进行了比较详细的实验和总结。

可以根据实际需要去显式地配置DeleteBehavior。另外,习惯观察生成的Migrations文件,也是学习EFCore一些惯例或者说默认配置的很好的方法。

验证

启动Api项目,发送DELETE请求:

请求

响应

并且从数据库里我们以可以发现,这条TodoList下包含的TodoItem也被一同删除了。

总结

到此为止HTTP常用的四大请求我们已经通过几个例子讲完了,关于HEAD请求OPTION请求以及遗留的PATCH请求后面会写完。下一篇文章开始我们一起学习如何使用FluentValidation来进行请求参数校验。

关于HTTP请求幂等性的介绍

首先明确两个概念:

1.什么叫做HTTP请求是否安全:如果我们执行请求后,对应的资源实体不发生改变,我们称这个请求是安全的;

2.什么叫做HTTP请求是否幂等:对于执行请求后产生的副作用(即指如果请求不安全,则称其会产生副作用),请求执行一次和执行多次的副作用是相同的,我们称这个请求是幂等的,很显然安全的请求一定是幂等的。

了解了这两个概念后,我们直接来看这张表格,快速地对HTTP请求的安全性和幂等性有一个认识。

鉴于PATCH方法并不常用,那么重点需要关注的主要就是POST请求,以及一部分的PUT请求(比如更新的字段是在当前字段值的基础上进行计算而新得出这种场景,实际就不是幂等的,但是我们一般不推荐在Update时做这种类型的操作,更推荐的是把计算逻辑前置到接口响应处理前,以整体设置值的方式去Update实体)。一般而言POST请求是用来创建资源的,如果不采取某种方式来保证执行结果的实际幂等性,那么该请求产生的副作用将是难以控制和处理的。

如何保证接口的幂等性?

正式因为并非所有的HTTP请求(在这里我们可以泛化到任意类型的接口请求)都是幂等的,而不管是应用程序内的容错还是服务之间因为分区导致的对请求的幂等性更为严格的要求(尤其是在分布式系统中,对于分区导致的请求重试的场景),我们需要在设计和实现接口的时候,把幂等性设计考虑进来,提高接口的鲁棒性。

总体来说,实现接口的幂等性有两种思路:一种是通过代码逻辑去限制重复调用出现的副作用;第二种是通过Token唯一性来保证同一个请求不会被调用多次。

通过代码逻辑去限制副作用的实现方式有很多种:从前端/界面的层次上去人为限制请求的重复发送(比如按钮置灰禁止点击之类的),通过在数据库层面应用锁或使用唯一索引,通过在逻辑执行过程中应用锁等方式。这种方式只能针对一些通过满足某些判断进行的逻辑实现,有其局限性。

通过Token唯一性保证幂等的思路大致是这样:生成全局唯一的Token保存下来,并在前端页面获取保存,发送请求时连同Token一起发到后端,后端先进行Token校验,校验通过发送实际请求或执行逻辑,完成后删除旧Token并生成新Token,那么前端下次携带保存的旧Token来请求时,因为Token校验不通过而拒绝继续执行。这种方式就好比短信验证码,只有第一次携带这个验证码请求时会成功,后端判断第一次请求有效后就会把这个验证码置为无效,下次你就无法携带相同的验证码继续发起请求了。例子不是特别恰当,但是可以类比着进行理解。 

到此这篇关于.NET 6开发TodoList应用之实现DELETE请求与HTTP请求幂等性的文章就介绍到这了,更多相关.NET 6实现DELETE请求与HTTP请求幂等性内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

您可能感兴趣的文章:
  • .NET 6开发TodoList应用之实现查询分页
  • .NET 6开发TodoList应用之实现ActionFilter
  • .NET 6开发TodoList应用之实现接口请求验证
  • .NET 6开发TodoList应用之实现PUT请求
  • .NET 6开发TodoList应用之实现全局异常处理
  • .NET 6开发TodoList应用之使用AutoMapper实现GET请求
  • .NET 6开发TodoList应用之实现Repository模式
  • .NET 6开发TodoList应用之使用MediatR实现POST请求
  • .NET 6开发TodoList应用引入数据存储
  • .NET 6开发TodoList应用引入第三方日志库
  • .NET 6开发TodoList应用实现结构搭建
  • .NET 6开发TodoList应用实现系列背景
  • 使用.NET 6开发TodoList应用之引入数据存储的思路详解
  • 使用.NET 6开发TodoList应用之领域实体创建原理和思路
  • .NET 6开发TodoList应用之请求日志组件HttpLogging介绍

ajax – 带有正文的Angular $http.delete请求

ajax – 带有正文的Angular $http.delete请求

所以我查看了这篇文章:
is an entity body allowed for an http delete request

这似乎表明,虽然在某些概念层面上做“好”,但在实践中它可能不可行,因为浏览器只是忽略它.

我有一些express.js身份验证中间件我需要通过,我不想将我的用户详细信息附加到url params.我需要进行身份验证的所有其他请求都会将这些详细信息附加到请求正文中.

有没有办法强迫这个?我看到其他一些帖子,其中有些人似乎成功通过删除请求传递了一个正文.

我正在运行一个节点/ sails后端.它始终将正文记录为未定义的删除请求.有没有办法修改

解决方法

sails API从params中提取要删除的对象的id,因此我们必须将id附加到url.

但是如果我想在处理删除请求之前在服务器端验证中传递一些身份验证详细信息,我不能只将它们作为删除请求的第二个参数添加到对象中,就像你可以使用$http.post一样.

Angular的post方法自动将我们插入的任何内容作为第二个参数分配给请求的主体,但delete方法不会.

Angular的$http.delete方法允许我们提供一个配置对象作为第二个参数,通过它我们可以访问’data’属性.这与通过它的第二个参数发布的方式相同.

因此,如果我们需要将主体附加到删除请求,我们可以使用以下内容:

$http.delete('/api/' + objectToDelete.id,{data: {id: currentUser().id,level: currentUser().level}});

这会将对象传递给url参数中的delete id,并将body中的用户凭据作为对象传递.

ajax 参数传递之 [HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete] 请求

ajax 参数传递之 [HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete] 请求

复制代码
  $.ajax({
        type: "get",
        url: "http://localhost:27221/api/Charging/GetByModel",
        contentType: "application/json",
        data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });
复制代码
[HttpGet]
        public string GetAllChargingData([FromUri]TB_CHARGING obj)
        {
            return "ChargingData" + obj.ID;
        }

得到结果:

 

如果你不想使用 [FromUri] 这些在参数里面加特性的这种 “怪异” 写法,也可以采用先序列化,再在后台反序列的方式。

复制代码
  $.ajax({
        type: "get",
        url: "http://localhost:27221/api/Charging/GetByModel",
        contentType: "application/json",
        data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });
复制代码
[HttpGet]
        public string GetByModel(string strQuery)
        {
            TB_CHARGING oData = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_CHARGING>(strQuery);
            return "ChargingData" + oData.ID;
        }

这样在后台得到我们序列化过的对象,再通过反序列化就能得到对象。

 

数组作为参数

一般 get 请求不建议将数组作为参数,因为我们知道 get 请求传递参数的大小是有限制的,最大 1024 字节,数组里面内容较多时,将其作为参数传递可能会发生参数超限丢失的情况。

 

 

复制代码
$.ajax({
        type: "get",
        url: "http://localhost:27221/api/Charging/FindByModel",
        contentType: "application/json",
        data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });
复制代码
[HttpGet]
        public string FindByModel(string strQuery)
        {
            TB_CHARGING oData = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_CHARGING>(strQuery);
            return "ChargingData" + oData.ID;
        }

方法名以 Get 开头,WebApi 会自动默认这个请求就是 get 请求,而如果你以其他名称开头而又不标注方法的请求方式,那么这个时候服务器虽然找到了这个方法,但是由于请求方式不确定,所以直接返回给你 405—— 方法不被允许的错误。

最后结论:所有的 WebApi 方法最好是加上请求的方式([HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]),不要偷懒,这样既能防止类似的错误,也有利于方法的维护,别人一看就知道这个方法是什么请求。

二、post 请求

在 WebApi 的 RESETful 风格里面,API 服务的增删改查,分别对应着 http 的 post/delete/put/get 请求。我们下面就来说说 post 请求参数的传递方式。

1、基础类型参数

 post 请求的基础类型的参数和 get 请求有点不一样,我们知道 get 请求的参数是通过 url 来传递的,而 post 请求则是通过 http 的请求体中传过来的,WebApi 的 post 请求也需要从 http 的请求体里面去取参数。

(1)错误的写法

复制代码
$.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { NAME: "Jim" },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });

        [HttpPost]
        public bool SaveData(string NAME)
        {
            return true;
        }
复制代码

这是一种看上去非常正确的写法,可是实际情况是:

(2)正确的用法

复制代码
$.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { "": "Jim" },
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData([FromBody]string NAME)
        {
            return true;
        }
复制代码

这是一种另许多人头痛的写法,但是没办法,这样确实能得到我们的结果:

我们一般的通过 url 取参数的机制是键值对,即某一个 key 等于某一个 value,而这里的 FromBody 和我们一般通过 url 取参数的机制则不同,它的机制是 = value,没有 key 的概念,并且如果你写了 key (比如你的 ajax 参数写的 {NAME:"Jim"}),后台反而得到的 NAME 等于 null。不信你可以试试。

上面讲的都是传递一个基础类型参数的情况,那么如果我们需要传递多个基础类型呢?按照上面的推论,是否可以 ([FromBody] string NAME, [FromBody] string DES) 这样写呢。试试便知。

(1)错误写法

复制代码
$.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { "": "Jim","":"备注" },
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData([FromBody]string NAME, [FromBody] string DES)
        {
            return true;
        }
复制代码

得到结果

这说明我们没办法通过多个 [FromBody] 里面取值,此法失败。

(2)正确用法

既然上面的办法行不通,那我们如何传递多个基础类型的数据呢?很多的解决办法是新建一个类去包含传递的参数,博主觉得这样不够灵活,因为如果我们前后台每次传递多个参数的 post 请求都去新建一个类的话,我们系统到时候会有多少个这种参数类?维护起来那是相当的麻烦的一件事!所以博主觉得使用 dynamic 是一个很不错的选择。我们来试试。

复制代码
$.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify({ NAME: "Jim",DES:"备注" }),
        success: function (data, status) {}
    });

        [HttpPost]
        public object SaveData(dynamic obj)
        {
            var strName = Convert.ToString(obj.NAME);
            return strName;
        }
复制代码

通过 dynamic 动态类型能顺利得到多个参数,省掉了 [FromBody] 这个累赘,并且 ajax 参数的传递不用使用 "无厘头" 的 {"":"value"} 这种写法,有没有一种小清新的感觉~~有一点需要注意的是这里在 ajax 的请求里面需要加上参数类型为 Json,即 contentType: ''application/json'', 这个属性。

通过上文 post 请求基础类型参数的传递,我们了解到了 dynamic 的方便之处,为了避免 [FromBody] 这个累赘和 {"":"value"} 这种" 无厘头 " 的写法。博主推荐所有基础类型使用 dynamic 来传递,方便解决了基础类型一个或多个参数的传递,示例如上文

 

2、实体作为参数

(1)单个实体作为参数

上面我们通过 dynamic 类型解决了 post 请求基础类型数据的传递问题,那么当我们需要传递一个实体作为参数该怎么解决呢?我们来看下面的代码便知:

复制代码
$.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData(TB_CHARGING oData)
        {
            return true;
        }
复制代码

得到结果

原理解释:使用实体作为参数的时候,前端直接传递普通 json,后台直接使用对应的类型去接收即可,不用 FromBody。但是这里需要注意的一点就是,这里不能指定 contentType 为 appplication/json,否则,参数无法传递到后台。我们来看看它默认的 contentType 是什么:

 

为了弄清楚原因,博主查了下 http 的 Content-Type 的类型。看到如下说明:

  • application/x-www-form-urlencoded : <form encType=””> 中默认的 encType,form 表单数据被编码为 key/value 格式发送到服务器(表单默认的提交数据的格式);
  • application/json    : JSON 数据格式

也就是说 post 请求默认是将表单里面的数据的 key/value 形式发送到服务,而我们的服务器只需要有对应的 key/value 属性值的对象就可以接收到。而如果使用 application/json,则表示将前端的数据以序列化过的 json 传递到后端,后端要把它变成实体对象,还需要一个反序列化的过程。按照这个逻辑,那我们如果指定 contentType 为 application/json,然后传递序列化过的对象应该也是可以的啊。博主好奇心重,还是打算一试到底,于是就有了下面的代码:

复制代码
var postdata = { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify(postdata),
        success: function (data, status) {}
    });
        [HttpPost]
        public bool SaveData(TB_CHARGING lstCharging)
        {
            return true;
        }
复制代码

得到结果:

尝试成功,也就是说,两种写法都是可行的。如果你指定了 contentType 为 application/json,则必须要传递序列化过的对象;如果使用 post 请求的默认参数类型,则前端直接传递 json 类型的对象即可。 

(2)实体和基础类型一起作为参数传递

有些时候,我们需要将基础类型和实体一起传递到后台,这个时候,我们神奇的 dynamic 又派上用场了。

复制代码
var postdata = { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify({ NAME:"Lilei", Charging:postdata }),
        success: function (data, status) {}
    });

        [HttpPost]
        public object SaveData(dynamic obj)
        {
            var strName = Convert.ToString(obj.NAME);
            var oCharging = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_CHARGING>(Convert.ToString(obj.Charging));
            return strName;
        }
复制代码

得到结果:

3、数组作为参数

(1)基础类型数组

复制代码
var arr = ["1", "2", "3", "4"];
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify(arr),
        success: function (data, status) { }
    });

        [HttpPost]
        public bool SaveData(string[] ids)
        {
            return true;
        }
复制代码

得到结果:

(2)实体集合

复制代码
var arr = [
        { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        { ID: "2", NAME: "Lilei", CREATETIME: "1990-12-11" },
        { ID: "3", NAME: "Lucy", CREATETIME: "1986-01-10" }
    ];
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify(arr),
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData(List<TB_CHARGING> lstCharging)
        {
            return true;
        }
复制代码

得到结果:

4、后台发送请求参数的传递

上面写了那么多,都是通过前端的 ajax 请求去做的,我们知道,如果调用方不是 web 项目,比如 Android 客户端,可能需要从后台发送 http 请求来调用我们的接口方法,如果我们通过后台去发送请求是否也是可行的呢?我们以实体对象作为参数来传递写写代码试一把。

复制代码
 public void TestReques()
    {
         //请求路径
            string url = "http://localhost:27221/api/Charging/SaveData";

            //定义request并设置request的路径
            WebRequest request = WebRequest.Create(url);
            request.Method = "post";

            //初始化request参数
            string postData = "{ ID: \"1\", NAME: \"Jim\", CREATETIME: \"1988-09-11\" }";

            //设置参数的编码格式,解决中文乱码
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

            //设置request的MIME类型及内容长度
            request.ContentType = "application/json";
            request.ContentLength = byteArray.Length;

            //打开request字符流
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            //定义response为前面的request响应
            WebResponse response = request.GetResponse();

            //获取相应的状态代码
            Console.WriteLine(((HttpWebResponse)response).StatusDescription);

            //定义response字符流
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();//读取所有
            Console.WriteLine(responseFromServer);
    }
复制代码

当代码运行到 request.GetResponse () 这一句的时候,API 里面进入断点

尝试成功。

三、put 请求

WebApi 里面 put 请求一般用于对象的更新。它和用法和 post 请求基本相同。同样支持 [FromBody],同样可以使用 dynamic。

1、基础类型参数

复制代码
$.ajax({
        type: "put",
        url: "http://localhost:27221/api/Charging/Update",
        contentType: ''application/json'',
        data: JSON.stringify({ ID: "1" }),
        success: function (data, status) {}
    });

        [HttpPut]
        public bool Update(dynamic obj )
        {
            return true;
        }
复制代码

2、实体作为参数

和 post 请求相同。

3、数组作为参数

和 post 请求相同。

四、delete 请求

顾名思义,delete 请求肯定是用于删除操作的。参数传递机制和 post 也是基本相同。下面简单给出一个例子,其他情况参考 post 请求。

复制代码
var arr = [
        { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        { ID: "2", NAME: "Lilei", CREATETIME: "1990-12-11" },
        { ID: "3", NAME: "Lucy", CREATETIME: "1986-01-10" }
    ];
    $.ajax({
        type: "delete",
        url: "http://localhost:27221/api/Charging/OptDelete",
        contentType: ''application/json'',
        data: JSON.stringify(arr),
        success: function (data, status) {}
    });

        [HttpDelete]
        public bool OptDelete(List<TB_CHARGING> lstChargin)
        {
            return true;
        }
复制代码

 

博文的一点评论:

dynamic 虽然很巧妙地解决了从请求 body 中获取参数。但是这样不利于生成 api 文档。
ps:一般都是用 pagehelp 等工具,自动读取元数据,生成 api 文档的。
如果用 dynamic 的话,生成的文档就只有一个 dynamic 类型的参数,这样 api 调用者就无法明确 api 的参数了。

还有接收参数定义为 dynamic 类型的话,后续维护或升级太麻烦。

 

如果是 post 操作。文中介绍了两种传递实体参数的方法
第一种:

1
data: { ID:  "1" , NAME:  "Jim" , CREATETIME:  "1988-09-11"  }


不需要指定 contentType。
第二种:

1
data:  "{\"SoldToParty\":\"fdsfds\",\"SaleOrg\":\"fdsfds\",\"Channel\":\"ffffff\",\"Division\":\"dfdfsdsfd\"}"


需要指定 contentType:"application/json"。两种方式后台都可以得到实体。不知道你的 Json 字符串是怎么得到的,是拼的字符串?建议使用 JSON.stringify ({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11"}) 这种方式得到 json 字符串。

 

 HttpPost 传单个变量

[HttpPost]
public JsonResult DeleteFrequencyInfo(List<string> ids)

$.ajax(
{
url: "/Frequency/DeleteFrequencyInfo",
type: "post",
data: { "ids": deleted },
dataType: "json",

HttpPost 传实体

[HttpPost]
public JsonResult GetFrequencyPrint(FrequencyPrintQO request)

public class FrequencyPrintQO
{
public string FrequencyId { get; set; }
public List<string> Freedombarcodes { get; set; }
}

$.ajax({
type: "POST",
url: ''/Print/GetFrequencyPrint'',
dataType: "Json",
async: false, // 此处必须定义为同步
data: {Freedombarcodes: freedombarcodes},

 

ajax参数传递之[HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]请求

ajax参数传递之[HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]请求

$.ajax({ type: "get", url: "http://localhost:27221/api/Charging/GetByModel", contentType: "application/json", data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }, success: function (data, status) { if (status == "success") { $("#div_test").html(data); } } });

复制代码

       [HttpGet]
        public string GetAllChargingData([FromUri]TB_CHARGING obj)
        {
            return "ChargingData" + obj.ID;
        }

得到结果:

 

如果你不想使用[FromUri]这些在参数里面加特性的这种“怪异”写法,也可以采用先序列化,再在后台反序列的方式。

复制代码

  $.ajax({
        type: "get",
        url: "http://localhost:27221/api/Charging/GetByModel",
        contentType: "application/json",
        data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });

复制代码

复制代码

        [HttpGet]
        public string GetByModel(string strQuery)
        {
            TB_CHARGING oData = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_CHARGING>(strQuery);
            return "ChargingData" + oData.ID;
        }

复制代码

这样在后台得到我们序列化过的对象,再通过反序列化就能得到对象。

 

数组作为参数

一般get请求不建议将数组作为参数,因为我们知道get请求传递参数的大小是有限制的,最大1024字节,数组里面内容较多时,将其作为参数传递可能会发生参数超限丢失的情况。

 

 

复制代码

    $.ajax({
        type: "get",
        url: "http://localhost:27221/api/Charging/FindByModel",
        contentType: "application/json",
        data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });

复制代码

复制代码

        [HttpGet]
        public string FindByModel(string strQuery)
        {
            TB_CHARGING oData = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_CHARGING>(strQuery);
            return "ChargingData" + oData.ID;
        }

复制代码

方法名以Get开头,WebApi会自动默认这个请求就是get请求,而如果你以其他名称开头而又不标注方法的请求方式,那么这个时候服务器虽然找到了这个方法,但是由于请求方式不确定,所以直接返回给你405——方法不被允许的错误。

最后结论:所有的WebApi方法最好是加上请求的方式([HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]),不要偷懒,这样既能防止类似的错误,也有利于方法的维护,别人一看就知道这个方法是什么请求。

二、post请求

在WebApi的RESETful风格里面,API服务的增删改查,分别对应着http的post/delete/put/get请求。我们下面就来说说post请求参数的传递方式。

1、基础类型参数

 post请求的基础类型的参数和get请求有点不一样,我们知道get请求的参数是通过url来传递的,而post请求则是通过http的请求体中传过来的,WebApi的post请求也需要从http的请求体里面去取参数。

(1)错误的写法

复制代码

    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { NAME: "Jim" },
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        }
    });

        [HttpPost]
        public bool SaveData(string NAME)
        {
            return true;
        }

复制代码

这是一种看上去非常正确的写法,可是实际情况是:

(2)正确的用法

复制代码

    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { "": "Jim" },
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData([FromBody]string NAME)
        {
            return true;
        }

复制代码

这是一种另许多人头痛的写法,但是没办法,这样确实能得到我们的结果:

我们一般的通过url取参数的机制是键值对,即某一个key等于某一个value,而这里的FromBody和我们一般通过url取参数的机制则不同,它的机制是=value,没有key的概念,并且如果你写了key(比如你的ajax参数写的{NAME:"Jim"}),后台反而得到的NAME等于null。不信你可以试试。

上面讲的都是传递一个基础类型参数的情况,那么如果我们需要传递多个基础类型呢?按照上面的推论,是否可以([FromBody]string NAME, [FromBody]string DES)这样写呢。试试便知。

(1)错误写法

复制代码

    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { "": "Jim","":"备注" },
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData([FromBody]string NAME, [FromBody] string DES)
        {
            return true;
        }

复制代码

得到结果

这说明我们没办法通过多个[FromBody]里面取值,此法失败。

(2)正确用法

既然上面的办法行不通,那我们如何传递多个基础类型的数据呢?很多的解决办法是新建一个类去包含传递的参数,博主觉得这样不够灵活,因为如果我们前后台每次传递多个参数的post请求都去新建一个类的话,我们系统到时候会有多少个这种参数类?维护起来那是相当的麻烦的一件事!所以博主觉得使用dynamic是一个很不错的选择。我们来试试。

复制代码

    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify({ NAME: "Jim",DES:"备注" }),
        success: function (data, status) {}
    });

        [HttpPost]
        public object SaveData(dynamic obj)
        {
            var strName = Convert.ToString(obj.NAME);
            return strName;
        }

复制代码

通过dynamic动态类型能顺利得到多个参数,省掉了[FromBody]这个累赘,并且ajax参数的传递不用使用"无厘头"的{"":"value"}这种写法,有没有一种小清新的感觉~~有一点需要注意的是这里在ajax的请求里面需要加上参数类型为Json,即 contentType: ''application/json'', 这个属性。

通过上文post请求基础类型参数的传递,我们了解到了dynamic的方便之处,为了避免[FromBody]这个累赘和{"":"value"}这种"无厘头"的写法。博主推荐所有基础类型使用dynamic来传递,方便解决了基础类型一个或多个参数的传递,示例如上文

 

2、实体作为参数

(1)单个实体作为参数

上面我们通过dynamic类型解决了post请求基础类型数据的传递问题,那么当我们需要传递一个实体作为参数该怎么解决呢?我们来看下面的代码便知:

复制代码

    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData(TB_CHARGING oData)
        {
            return true;
        }

复制代码

得到结果

原理解释:使用实体作为参数的时候,前端直接传递普通json,后台直接使用对应的类型去接收即可,不用FromBody。但是这里需要注意的一点就是,这里不能指定contentType为appplication/json,否则,参数无法传递到后台。我们来看看它默认的contentType是什么:

 

为了弄清楚原因,博主查了下http的Content-Type的类型。看到如下说明:

  • application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式);
  • application/json    : JSON数据格式

也就是说post请求默认是将表单里面的数据的key/value形式发送到服务,而我们的服务器只需要有对应的key/value属性值的对象就可以接收到。而如果使用application/json,则表示将前端的数据以序列化过的json传递到后端,后端要把它变成实体对象,还需要一个反序列化的过程。按照这个逻辑,那我们如果指定contentType为application/json,然后传递序列化过的对象应该也是可以的啊。博主好奇心重,还是打算一试到底,于是就有了下面的代码:

复制代码

var postdata = { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify(postdata),
        success: function (data, status) {}
    });
        [HttpPost]
        public bool SaveData(TB_CHARGING lstCharging)
        {
            return true;
        }

复制代码

得到结果:

尝试成功,也就是说,两种写法都是可行的。如果你指定了contentType为application/json,则必须要传递序列化过的对象;如果使用post请求的默认参数类型,则前端直接传递json类型的对象即可。 

(2)实体和基础类型一起作为参数传递

有些时候,我们需要将基础类型和实体一起传递到后台,这个时候,我们神奇的dynamic又派上用场了。

复制代码

var postdata = { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify({ NAME:"Lilei", Charging:postdata }),
        success: function (data, status) {}
    });

        [HttpPost]
        public object SaveData(dynamic obj)
        {
            var strName = Convert.ToString(obj.NAME);
            var oCharging = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_CHARGING>(Convert.ToString(obj.Charging));
            return strName;
        }

复制代码

得到结果:

3、数组作为参数

(1)基础类型数组

复制代码

var arr = ["1", "2", "3", "4"];
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify(arr),
        success: function (data, status) { }
    });

        [HttpPost]
        public bool SaveData(string[] ids)
        {
            return true;
        }

复制代码

得到结果:

(2)实体集合

复制代码

var arr = [
        { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        { ID: "2", NAME: "Lilei", CREATETIME: "1990-12-11" },
        { ID: "3", NAME: "Lucy", CREATETIME: "1986-01-10" }
    ];
    $.ajax({
        type: "post",
        url: "http://localhost:27221/api/Charging/SaveData",
        contentType: ''application/json'',
        data: JSON.stringify(arr),
        success: function (data, status) {}
    });

        [HttpPost]
        public bool SaveData(List<TB_CHARGING> lstCharging)
        {
            return true;
        }

复制代码

得到结果:

4、后台发送请求参数的传递

上面写了那么多,都是通过前端的ajax请求去做的,我们知道,如果调用方不是web项目,比如Android客户端,可能需要从后台发送http请求来调用我们的接口方法,如果我们通过后台去发送请求是否也是可行的呢?我们以实体对象作为参数来传递写写代码试一把。

复制代码

 public void TestReques()
    {
         //请求路径
            string url = "http://localhost:27221/api/Charging/SaveData";

            //定义request并设置request的路径
            WebRequest request = WebRequest.Create(url);
            request.Method = "post";

            //初始化request参数
            string postData = "{ ID: \"1\", NAME: \"Jim\", CREATETIME: \"1988-09-11\" }";

            //设置参数的编码格式,解决中文乱码
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

            //设置request的MIME类型及内容长度
            request.ContentType = "application/json";
            request.ContentLength = byteArray.Length;

            //打开request字符流
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            //定义response为前面的request响应
            WebResponse response = request.GetResponse();

            //获取相应的状态代码
            Console.WriteLine(((HttpWebResponse)response).StatusDescription);

            //定义response字符流
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();//读取所有
            Console.WriteLine(responseFromServer);
    }

复制代码

当代码运行到request.GetResponse()这一句的时候,API里面进入断点

尝试成功。

三、put请求

WebApi里面put请求一般用于对象的更新。它和用法和post请求基本相同。同样支持[FromBody],同样可以使用dynamic。

1、基础类型参数

复制代码

    $.ajax({
        type: "put",
        url: "http://localhost:27221/api/Charging/Update",
        contentType: ''application/json'',
        data: JSON.stringify({ ID: "1" }),
        success: function (data, status) {}
    });

        [HttpPut]
        public bool Update(dynamic obj )
        {
            return true;
        }

复制代码

2、实体作为参数

和post请求相同。

3、数组作为参数

和post请求相同。

四、delete请求

顾名思义,delete请求肯定是用于删除操作的。参数传递机制和post也是基本相同。下面简单给出一个例子,其他情况参考post请求。

复制代码

var arr = [
        { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },
        { ID: "2", NAME: "Lilei", CREATETIME: "1990-12-11" },
        { ID: "3", NAME: "Lucy", CREATETIME: "1986-01-10" }
    ];
    $.ajax({
        type: "delete",
        url: "http://localhost:27221/api/Charging/OptDelete",
        contentType: ''application/json'',
        data: JSON.stringify(arr),
        success: function (data, status) {}
    });

        [HttpDelete]
        public bool OptDelete(List<TB_CHARGING> lstChargin)
        {
            return true;
        }

复制代码

 

博文的一点评论:

dynamic虽然很巧妙地解决了从请求body中获取参数。但是这样不利于生成api文档。
ps:一般都是用pagehelp等工具,自动读取元数据,生成api文档的。
如果用dynamic的话,生成的文档就只有一个dynamic类型的参数,这样api调用者就无法明确api的参数了。

还有接收参数定义为dynamic类型的话,后续维护或升级太麻烦。

 

如果是post操作。文中介绍了两种传递实体参数的方法
第一种:

1

data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }


不需要指定contentType。
第二种:

1

data: "{\"SoldToParty\":\"fdsfds\",\"SaleOrg\":\"fdsfds\",\"Channel\":\"ffffff\",\"Division\":\"dfdfsdsfd\"}"


需要指定contentType:"application/json"。两种方式后台都可以得到实体。不知道你的Json字符串是怎么得到的,是拼的字符串?建议使用JSON.stringify( { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" })这种方式得到json字符串。

今天关于是否允许实体正文进行HTTP DELETE请求?是否允许从此web站点访问下列应用程序提示去掉的讲解已经结束,谢谢您的阅读,如果想了解更多关于.NET 6开发TodoList应用之实现DELETE请求与HTTP请求幂等性、ajax – 带有正文的Angular $http.delete请求、ajax 参数传递之 [HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete] 请求、ajax参数传递之[HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]请求的相关知识,请在本站搜索。

本文标签: