日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

ASP.NET WebAPI 中的参数绑定

發(fā)布時(shí)間:2023/12/4 asp.net 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET WebAPI 中的参数绑定 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

當(dāng) WebAPI 調(diào)用 Controller 上的方法時(shí), 必須為其參數(shù)賦值, 這個(gè)過程就是參數(shù)綁定。 本文介紹 WebAPI 如何綁定參數(shù), 以及如何進(jìn)行自定義。

WebAPI 默認(rèn)使用下面的規(guī)則進(jìn)行參數(shù)綁定:

  • 簡(jiǎn)單類型, WebAPI 嘗試從 URL 中獲取它的值。 簡(jiǎn)單類型包括:

    • .NET?原始類型(int、?bool、?float、?double?等);

    • 以及?TimeSpan?、?DateTime?、?Guid、?decimal?和?string;

    • 提供了類型轉(zhuǎn)換器 (Type Converter), 能夠從字符串轉(zhuǎn)換的類型。

  • 復(fù)雜類型則使用?media-type formatter?從 HTTP 請(qǐng)求的正文 (body) 中讀取。

比如一個(gè)典型的 WebAPI 方法:

IHttpActionResult Put(int id, Product item) { ... }

參數(shù)?id?是一個(gè)簡(jiǎn)單類型, 所以從 request URI 中取值, 而參數(shù)?item?是復(fù)雜類型, 則從 request 正文 (body) 中取值。

使用 [FromUri]

要強(qiáng)制 WebAPI 從 URL 讀取一個(gè)復(fù)雜類型的參數(shù), 則需要在該參數(shù)上添加?FromUri?標(biāo)記。 下面的例子定義了一個(gè)?GeoPoint?類型, 以及如何從 URI 中獲取?GeoPoint?實(shí)例。

public class GeoPoint {public double Latitude { get; set; }public double Longitude { get; set; }}public class TestController : ApiController {public IHttpActionResult Get([FromUri]GeoPoint location) { ... }}

客戶端可以在 QueryString 中傳遞 Latitude 和 Longitude 來(lái)構(gòu)造 GeoPoint 實(shí)例, 示例請(qǐng)求如下:

http://127.0.0.1/api/test?latitude=22.3&longitude=113.2

注: QueryString 中的參數(shù)名稱是不區(qū)分大小寫的。

對(duì)于數(shù)組類型, 也可以使用?[FromUri]?標(biāo)記, 比如:

public IHttpActionResult Get([FromUri]int[] items) { ... }

客戶端這樣發(fā)送請(qǐng)求:

http://127.0.0.1/api/test?items=1&items=2&items=3

服務(wù)端就可以接收到數(shù)組參數(shù)了。

使用 [FromBody]

要強(qiáng)制 WebAPI 從 request正文 (body) 中讀取一個(gè)簡(jiǎn)單類型的參數(shù), 需要在該參數(shù)上添加?FromBody?標(biāo)記:

public HttpResponseMessage Post([FromBody] string name) { ... }

在這個(gè)例子中, WebAPI 需要使用?media-type formatter?從 request正文 (body) 中讀取?name?的值, 示例請(qǐng)求如下:

POST http://localhost:5076/api/values HTTP/1.1User-Agent: FiddlerHost: localhost:5076Content-Type: application/json Content-Length: 7"Alice"

當(dāng)一個(gè)參數(shù)有?[FromBody]?標(biāo)記時(shí), WebAPI 使用?Content-Type?標(biāo)頭來(lái)選擇正確的格式, 在上面的例子中, Content-Type?是?application/json?, request正文 (body) 的內(nèi)容是原始的 JSON 字符串, 而不是一個(gè) JSON 對(duì)象。

> 一個(gè)函數(shù)中, 最多只能有一個(gè)?[FromBody]?標(biāo)記, 因?yàn)榭蛻舳说恼?qǐng)求有可能沒有緩沖, 只能被讀取一次。

使用 Type Converter

通過創(chuàng)建?Type Converter?, 實(shí)現(xiàn)從字符串轉(zhuǎn)換的方法, 可以讓 WebAPI 將復(fù)雜類型參數(shù)視為簡(jiǎn)單類型參數(shù)。

以上面的?GeoPoint?為例, 再提供一個(gè)?GeoPointConverter?實(shí)現(xiàn)從字符串到?GeoPoint?的轉(zhuǎn)換:

[TypeConverter(typeof(GeoPointConverter))]
public class GeoPoint {public double Latitude { get; set; }public double Longitude { get; set; }public bool TryParse(string s, out GeoPoint result) {result = null;var parts = s.Split(',');if (parts.Length != 2) {return false;}double latitude, longitude;if (double.TryParse(parts[0], out latitude) &&double.TryParse(parts[1], out longitude)) {result = new GeoPoint() { Longitude = longitude, Latitude = latitude };return true;}return false;}}public class GeoPointConverter : TypeConverter {public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType){if (sourceType == typeof(string)) {return true;}return base.CanConvertFrom(context, sourceType);}public override object ConvertFrom(ITypeDescriptorContext context, ? ? ? ?CultureInfo culture, object value) {if (value is string) {GeoPoint point;if (GeoPoint.TryParse((string)value, out point)) {return point;}}return base.ConvertFrom(context, culture, value);}}

現(xiàn)在, WebAPI 會(huì)將?GeoPoint?當(dāng)作簡(jiǎn)單類型, 意味著將嘗試從 URI 中綁定 GeoPoint 參數(shù)的值, 也不再需要?[FromUri]?標(biāo)記:

public HttpResponseMessage Get(GeoPoint location) { ... }

客戶端這樣發(fā)送 HTTP 請(qǐng)求:

https://127.0.0.1/api/test?location=22.3,113.2

使用 Model Binder

另一個(gè)比?type converter?更加靈活的是創(chuàng)建自定義?Model Binder?。 通過?Model Binder?, 可以直接訪問 http 請(qǐng)求、 action 描述以及路由的原始值。

要?jiǎng)?chuàng)建?Model Binder?, 需要實(shí)現(xiàn)接口?IModelBinder?, 它只定義了一個(gè)方法?BindModel?:

public interface IModelBinder {bool BindModel( ? ? ? ?HttpActionContext actionContext, ? ? ? ?ModelBindingContext bindingContext ? ?);}

下面是針對(duì)?GeoPoint?的實(shí)現(xiàn):

public class GeoPointModelBinder : IModelBinder {// List of known locations. ? ?private static ConcurrentDictionary<string, GeoPoint> _locations= new ConcurrentDictionary<string, GeoPoint>(StringComparer.OrdinalIgnoreCase);static GeoPointModelBinder() {_locations["redmond"] = new GeoPoint() { Latitude = 47.67856, Longitude = -122.131 };_locations["paris"] = new GeoPoint() { Latitude = 48.856930, Longitude = 2.3412 };_locations["tokyo"] = new GeoPoint() { Latitude = 35.683208, Longitude = 139.80894 };}public bool BindModel( ? ? ? ?HttpActionContext actionContext, ? ? ? ?ModelBindingContext bindingContext ? ?) {if (bindingContext.ModelType != typeof(GeoPoint)) {return false;}// exit if no value from value provider ? ? ? ?var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);if (val == null) {return false;}// exit if row value is not a string. ? ? ? ?string key = val.RawValue as string;if (key == null) {bindingContext.ModelState.AddModelError(bindingContext.ModelName,"Wrong value type");return false;}// ? ? ? ?GeoPoint result;if (_locations.TryGetValue(key, out result)|| GeoPoint.TryParse(key, out result)) {bindingContext.Model = result;return true;}// ? ? ? ?bindingContext.ModelState.AddModelError(bindingContext.ModelName,"Cannot convert value to Location");return false;}}

代碼很簡(jiǎn)單, 不必做太多的說明, Model Binder 不止局限于簡(jiǎn)單類型, 也支持復(fù)雜類型。 上面的 MobelBinder 支持兩種格式的查詢:

  • 使用已知的地名:?http://127.0.0.1:/rest/api/test?location=redmond?;

  • 使用經(jīng)緯度:?http://127.0.0.1:/rest/api/test?location=47.67856,-122.131?;

設(shè)置 Model Binder

首先, 可以在 action 方法的參數(shù)上添加?[ModelBinder]?標(biāo)記, 例如:

public HttpResponseMessage Get([ModelBinder(typeof(GeoPointModelBinder))] GeoPoint location)

其次, 可以在?GeoPoint?類型上添加 [ModelBinder] 標(biāo)記, 例如:

[ModelBinder(typeof(GeoPointModelBinder))]public class GeoPoint {// ....}

最后, 還可以在?HttpConfiguration?類中添加一個(gè)?model-binder provider?來(lái)使用, 代碼如下:

public static class WebApiConfig {public static void Register(HttpConfiguration config) {var provider = new SimpleModelBinderProvider(typeof(GeoPoint),new GeoPointModelBinder());config.Services.Insert(typeof(ModelBinderProvider),0,provider);// ... ? ?}}

在 action 方法中仍然需要為參數(shù)添加?[ModelBinder]?標(biāo)記, 來(lái)說明該參數(shù)需要使用?model-binder?來(lái)而不是?media formatter?來(lái)進(jìn)行參數(shù)綁定, 不過此時(shí)就不需要再指定 ModelBinder 的類型了:

public HttpResponseMessage Get( ? ?[ModelBinder] GeoPoint location) { ... }

使用 ValueProvider

Model Binder?需要從?Value Provider?中取值, 因此也可以創(chuàng)建自定義的?Value Provider?實(shí)現(xiàn)獲取特殊的值。 要實(shí)現(xiàn)自定義的?ValueProvider?, 需要實(shí)現(xiàn)接口?IValueProvider?, 下面是一個(gè)從 Cookie 中獲取值的?CookieValueProvider?:

public class CookieValueProvider : IValueProvider {private Dictionary<string, string> values;public CookieValueProvider(HttpActionContext actionContext) {if (actionContext == null) {throw new ArgumentNullException("actionContext");}values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);foreach (var cookie in actionContext.Request.Headers.GetCookies()) {foreach (CookieState state in cookie.Cookies) {values[state.Name] = state.Value;}}}public bool ContainsPrefix(string prefix) {return values.Keys.Contains(prefix);}public ValueProviderResult GetValue(string key) {string value;if (values.TryGetValue(key, out value)) {return new ValueProviderResult(value, value, CultureInfo.InvariantCulture);}return null;}}

同時(shí)還需要定義一個(gè)繼承自?ValueProviderFactory?的?CookieValueProviderFactory?, 代碼如下:

public class CookieValueProviderFactory : ValueProviderFactory {public override IValueProvider GetValueProvider(HttpActionContext actionContext) {return new CookieValueProvider(actionContext);}}

然后將?CookieValueProviderFactory?注冊(cè)到?HttpConfiguration?實(shí)例:

public static void Register(HttpConfiguration config) {config.Services.Add(typeof(ValueProviderFactory),new CookieValueProviderFactory());// ...}

Web API 將組合所有的?ValueProviderFactory?, 當(dāng)一個(gè) model binder 調(diào)用?ValueProvider.GetValue?方法時(shí), 將會(huì)收到第一個(gè)能夠提供對(duì)應(yīng)值的?ValueProviderFactory?提供的值。

或者, 也可以直接在在參數(shù)上使用?ValueProviderAttribute?標(biāo)記:

public HttpResponseMessage Get( ? ?[ValueProvider(typeof(CookieValueProviderFactory))] GeoPoint location) { ... }

這樣, Web API 在處理這個(gè)參數(shù)時(shí), 就會(huì)直接使用?CookieValueProviderFactory?, 不再使用其它的 CookieValueProviderFactory 。

HttpParameterBinding

Model binder?只是參數(shù)綁定中的一個(gè)特定的實(shí)例, 如果查看?ModelBinderAttribute?類的定義, 會(huì)發(fā)現(xiàn)它繼承自抽象類?ParameterBindingAttribute?, 這個(gè)類只定義了一個(gè)方法?GetBinding?, 返回一個(gè)?HttpParameterBinding?實(shí)例。

public abstract class ParameterBindingAttribute : Attribute {public abstract HttpParameterBinding GetBinding(HttpParameterDescriptor parameter);}

HttpParameterBinding?負(fù)責(zé)將參數(shù)綁定到值, 以?[ModelBinder]?為例, 這個(gè)標(biāo)記返回一個(gè)?HttpParameterBinding?實(shí)現(xiàn), 使用?IModelBinder?進(jìn)行具體的綁定。 當(dāng)然, 也可以實(shí)現(xiàn)自定義的?HttpParameterBinding?。

假設(shè)要獲取 HTTP 請(qǐng)求 Header 中的?if-match?和?if-none-match?標(biāo)簽 (ETag) , 先定義一個(gè)類來(lái)表示 ETag :

public class ETag {public string Tag { get; set; }}

同時(shí)再定義一個(gè)枚舉來(lái)指定是從?if-match?還是?if-none-match?標(biāo)頭中獲取 ETag:

public enum ETagMatch {IfMatch,IfNoneMatch}

接下來(lái)是從 HTTP 請(qǐng)求頭中獲取?ETag?的?ETagParameterBinding?,

public class ETagParameterBinding : HttpParameterBinding {ETagMatch match;public ETagParameterBinding( ? ? ? ?HttpParameterDescriptor parameter, ? ? ? ?ETagMatch match ? ?) : base(parameter) {match = match;}public override Task ExecuteBindingAsync( ? ? ? ?ModelMetadataProvider metadataProvider, ? ? ? ?HttpActionContext actionContext, ? ? ? ?CancellationToken cancellationToken ? ?) {EntityTagHeaderValue etagHeader = null;switch (match) {case ETagMatch.IfNoneMatch:etagHeader = actionContext.Request.Headers.IfNoneMatch.FirstOrDefault();break;case ETagMatch.IfMatch:etagHeader = actionContext.Request.Headers.IfMatch.FirstOrDefault();break;}ETag etag = null;if (etagHeader != null) {etag = new ETag { Tag = etagHeader.Tag };}actionContext.ActionArguments[Descriptor.ParameterName] = etag;var tsc = new TaskCompletionSource<object>();tsc.SetResult(null);return tsc.Task;}}

在?ExecuteBindingAsync?方法中實(shí)現(xiàn)具體的綁定, 在這個(gè)方法中, 將取得的參數(shù)的值存放到?HttpActionContext的?ActionArgument?字典中。

注意, 如果自定義的?HttpParameterBinding?需要從 HTTP 請(qǐng)求的正文 (body) 中讀取信息, 則需要重寫?WillReadBody?并返回?true?。 由于 HTTP 請(qǐng)求正文可能是個(gè)沒有緩沖的流, 只能讀取一次, 所以 Web API 加強(qiáng)了一個(gè)規(guī)則, 那就是每個(gè)方法只有一個(gè)綁定能夠從 HTTP 請(qǐng)求正文讀取數(shù)據(jù)。

要使用自定義的?HttpParameterBinding?, 則需要?jiǎng)?chuàng)建一個(gè)自定義的標(biāo)記, 繼承自?ParameterBindingAttribute。 針對(duì)上面的?ETagParameterBinding?, 我們來(lái)定義兩個(gè)自定義標(biāo)記, 分別表示從?if-match?和?if-none-match標(biāo)頭中獲取, 代碼如下:

public abstract class ETagMatchAttribute : ParameterBindingAttribute {private ETagMatch match;public ETagMatchAttribute(ETagMatch match) {match = match;}public override HttpParameterBinding GetBinding( ? ? ? ?HttpParameterDescriptor parameter ? ?) {if (parameter.ParameterType == typeof(ETag)) {return new ETagParameterBinding(parameter, match);}return parameter.BindAsError("Wrong parameter type");}}public class IfMatchAttribute : ETagMatchAttribute {public IfMatchAttribute() : base(ETagMatch.IfMatch) { }}public class IfNoneMatchAttribute : ETagMatchAttribute {public IfNoneMatchAttribute() : base(ETagMatch.IfNoneMatch) { }}

下面是一個(gè)使用?IfNoneMatch?的例子:

public HttpResponseMessage Get([IfNoneMatch] ETag etag) { ... }

除了直接使用這個(gè)標(biāo)記, 也可以在?HttpConfiguration?中進(jìn)行配置, 代碼如下:

config.ParameterBindingRules.Add(p => {if (p.ParameterType == typeof(ETag)&& p.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Get)) {return new ETagParameterBinding(p, ETagMatch.IfNoneMatch);}else {return null;}});

注意, 無(wú)法綁定時(shí), 一定要返回?null?。

IActionValueBinder

整個(gè)參數(shù)綁定的過程由一個(gè)叫做?IActionValueBinder?的可插拔的服務(wù)控制,默認(rèn)的按照下面的規(guī)則進(jìn)行參數(shù)綁定:

  • 在參數(shù)上查找 ` ParameterBindingAttribute?, 包括?[FromBody]?、?[FromUri]?、?[ModelBinder]` 或者其它自定義標(biāo)記;

  • 然后在?HttpConfiguration.ParameterBindingRules?中查找一個(gè)返回?HttpParameterBinding?實(shí)例的函數(shù);

  • 最后, 使用上面提到的默認(rèn)規(guī)則:

    • 如果參數(shù)是一個(gè)簡(jiǎn)單類型或者指定了類型轉(zhuǎn)換器, 從 URI 綁定, 相當(dāng)于在參數(shù)上添加?[FromUri]?標(biāo)記;

    • 否則, 嘗試從 HTTP 請(qǐng)求正文中讀取, 相當(dāng)于在參數(shù)上添加?[FromBody]?標(biāo)記。

  • 如果默認(rèn)的綁定不能滿足需求, 也可以實(shí)現(xiàn)自定義的?IActionValueBinder?來(lái)替換掉 Web API 默認(rèn)的實(shí)現(xiàn)。

    原文地址:http://beginor.github.io/2017/06/25/parameter-binding-in-aspnet-web-api.html


    .NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注

    總結(jié)

    以上是生活随笔為你收集整理的ASP.NET WebAPI 中的参数绑定的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。