我正在使用Microsoft Web API 2开发Web服务。
我有一个人控制器,我会处理补丁HTTP请求。在我的人员控制器下面:
namespace MyAssembly.WebApi.Controllers
{
[RoutePrefix("api/Person")]
public class PersonController : BaseController
{
[HttpGet]
public IHttpActionResult GetAll(int id)
{
// get logic...
}
[HttpPost]
[Route("api/Person/{id:int}")]
public IHttpActionResult Create(int id, Person dto)
{
// post logic...
}
[HttpPatch]
[Route("api/Person/{id:int}")]
public IHttpActionResult Update(int id, object dto)
{
Person currentPerson = myRepo.Get(id);
currentPerson.patch(dto); // <-- How can I "patch" currentPerson object?
myRepo.Update(id, currentPerson);
return Ok();
}
}
在我的人物对象下面:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
public int[] PreferredNumbers { get; set; }
public string[] PreferredSerieTV { get; set; }
public Address Address { get; set; }
}
我的目标是使用currentPerson
对象中的所有指定属性更新dto
对象的所有属性。
我的HTTP补丁请求应如下所示:
PATCH /api/Person/1
Host: localhost
Content-Type: application/json
Cache-Control: no-cache
{
"Name": "Alessia",
"PreferredNumbers": [1,2,3],
"PreferredSerieTV": ["Serie1", "Serie2"]
}
我尝试过使用Delta对象,但整数矩阵存在已知问题。以前我的签名如下:
public IHttpActionResult Update(int id, Delta<Person> dto) { ... }
很高兴:支持http请求的小写属性。
如何处理补丁请求?
[编辑]
我已经集成了您的解决方案,但我收到了此错误:
original
System.ArgumentException: 'Un oggetto di tipo 'Newtonsoft.Json.Linq.JArray' non può essere convertito nel tipo 'System.Int32[]'.'
traduction
System.ArgumentException: 'An object type 'Newtonsoft.Json.Linq.JArray' cannot be converted to type 'System.Int32[]'.'
引发异常的代码行是这样的:
private static void updateObjectPropertyCore<TObject>(TObject target, string propertyName, object value) where TObject : class
{
var type = typeof(TObject);
var property = type.GetPropertyCaseInsensitive(propertyName);
if (property != null && property.CanWrite)
{
property.SetValue(target, value); <---
}
}
非常感谢
答案 0 :(得分:1)
我为PUT和PATCH请求做了类似的事情。
仅接受属性以更改重构操作以接受IDictionary<string, object>
以保存已更改的属性以进行更新/修补。
[HttpPatch]
[Route("api/Person/{id:int}")]
public IHttpActionResult Update(int id, [FromBody] Dictionary<string, object> dto) { ... }
然后,可以使用以下扩展方法
来访问匹配属性public static void patchObject<TObject>(this TObject target, Dictionary<string, object> values) where TObject : class {
if (target != null) {
foreach (var kvp in values) {
updateObjectPropertyCore<TObject>(target, kvp.Key, kvp.Value);
}
}
}
private static void updateObjectPropertyCore<TObject>(TObject target, string propertyName, object value) where TObject : class {
var type = typeof(TObject);
var property = type.GetPropertyCaseInsensitive(propertyName);
if (property != null && property.CanWrite) {
object coercedValue;
var destinationType = property.PropertyType;
try {
coercedValue = Convert.ChangeType(value, destinationType, CultureInfo.CurrentCulture);
} catch {
return destinationType.IsValueType ? null : Activator.CreateInstance(destinationType);
}
property.SetValue(target, coercedValue);
}
}
/// <summary>
/// Gets a property by name, ignoring case and searching all interfaces.
/// </summary>
/// <param name="type">The type to inspect.</param>
/// <param name="propertyName">The property to search for.</param>
/// <returns>The property or null if not found.</returns>
public static PropertyInfo GetPropertyCaseInsensitive(this Type type, string propertyName) {
var typeList = new List<Type> { type };
if (type.IsInterface()) {
typeList.AddRange(type.GetInterfaces());
}
var flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance;
return typeList
.Select(interfaceType => interfaceType.GetProperty(propertyName, flags))
.FirstOrDefault(property => property != null);
}
所以现在你可以在要修补的对象上调用扩展方法。
[HttpPatch]
[Route("api/Person/{id:int}")]
public IHttpActionResult Update(int id, [FromBody] Dictionary<string, object> dto) {
var currentPerson = myRepo.Get(id);
if(currentPerson == null)
return NotFound();
currentPerson.patchObject(dto); // <-- matching keys in dto will modify target object
myRepo.Update(id, currentPerson);
return Ok();
}