Vendor-specificMediaType
目的:
- 为了在创建资源的时候可选择地带上某个属性,例如student资源中有一个IsOver90kg,需要可选择地带上。
- 需要两种创建资源的Model,即正常地StudentCreateDto和携带额外属性的StudentCreateWithWeightDto,再通过Content-Type进行选择。
- 不带额外属性的Content-Type为"application/json"和"application/vnd.niceboat.studentcreation+json"
- 带额外属性的为application/vnd.niceboat.studentcreationwithweight+json
- 不能破坏现有的Api约束
思路:
- 创建一个新的CreateModel,并且带有额外属性,做好AutoMapper。
- 创建一个新的Controller,且使用新建好的CreateModel
- 显然同样的创建资源的效果如果要使用不同的路由,并不合理,而且也不符合Vendor-specificMediaType的目的。
- 使用Attribute进行路由跳转
代码:
Attibute:
/// <summary>
/// IActionConstraint 接口实现order 和accept
/// </summary>
[AttributeUsage(AttributeTargets.All,Inherited =true,AllowMultiple = true)]
public class RequestHeaderMatchesMediaTypeAttibute : Attribute, IActionConstraint
{
private readonly string _requestHeaderToMatch;
private readonly MediaTypeCollection _mediaTypes = new MediaTypeCollection();
/// <summary>
/// attribute
/// </summary>
/// <param name="requestHeaderToMatch">content-type</param>
/// <param name="mediaType">主要的mediatype</param>
/// <param name="otherMediaTypes"></param>
public RequestHeaderMatchesMediaTypeAttibute(string requestHeaderToMatch
,string mediaType
,params string[] otherMediaTypes)
{
this._requestHeaderToMatch = requestHeaderToMatch ?? throw new ArgumentNullException(nameof(requestHeaderToMatch));
//获取转化后的数据并放入_mediaTypes
if (MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parseMediaType))
{
_mediaTypes.Add(parseMediaType);
}
else throw new ArgumentNullException(nameof(mediaType));
foreach (var otherMediaType in otherMediaTypes)
{
if (MediaTypeHeaderValue.TryParse(otherMediaType, out MediaTypeHeaderValue parseOtherMediaType))
{
_mediaTypes.Add(parseOtherMediaType);
}
else throw new ArgumentNullException(nameof(otherMediaTypes));
}
}
//优先级
public int Order => 0;
/// <summary>
/// 是否通过
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public bool Accept(ActionConstraintContext context)
{
var requestHeaders = context.RouteContext.HttpContext.Request.Headers;
if (!requestHeaders.ContainsKey(_requestHeaderToMatch)) return false;
var parsedRequestMediaType = new MediaType(requestHeaders[_requestHeaderToMatch]);
//如果_mediaTypes中存在对应的则返回true,否则不通过
foreach (var mediaType in _mediaTypes)
{
var parseMediaType = new MediaType(mediaType);
if (parsedRequestMediaType.Equals(parseMediaType))
{
return true;
}
}
return false;
}
}
Attibute的使用:
[RequestHeaderMatchesMediaTypeAttibute("Content-type"
,"application/json","application/vnd.niceboat.collegeforcreation+json")]
[Consumes("application/json", "application/vnd.niceboat.collegeforcreation+json")]