相信大家在專案中都用過統一響應引數模板。
先宣告一個響應模板類:
public class ResponseDto
{
public int code { get; set; }
public string msg { get; set; }
public object data { get; set; }
}
再定義返回成功和失敗的方法:
public IActionResult Success(object data)
{
return ......
}
public IActionResult Fail(string msg)
{
return ......
}
在介面返回時統一呼叫:
[HttpGet]
public IActionResult Get()
{
var data = new WeatherForecast() { Date = DateTime.Now };
return Success(data);
}
當然了,這篇文章所講的OutputFormatter
和上面的統一模板不衝突哈,存在共通之處,都是格式化響應引數嘛,拿來做個引子。
OutputFormatter
OutputFormatter
是所有格式化輸出的基類,有唯一的子類:TextOutputFormatter
,同時TextOutputFormatter
又有一大堆子類:
JsonOutputFormatter
NewtonsoftJsonOutputFormatter
StringOutputFormatter
SystemTextJsonOutputFormatter
XmlDataContractSerializerOutputFormatter
XmlSerializerOutputFormatter
如果不配置任何響應引數輸出格式,asp.net core api
響應引數預設的輸出格式就是json
。
猴:這個介面給我返回xml
,我不要json
。
我:你是不是腦子有毛病?好好的json
不用用xml
。
得,前端大佬得要求還是得滿足不是,這時候有些同學是不是已經去百度:.Net怎麼將物件轉換成xml?
No
No
No
,這時候就輪到OutputFormatter
的孫子 XmlDataContractSerializerOutputFormatter
出場了。
只需要簡單給介面配置一個屬性就搞定啦。
[Produces("application/xml")]
[HttpGet]
public WeatherForecast Get()
{
return new WeatherForecast() { Date = DateTime.Now };
}
我們來執行看一看:
wtf
,怎麼會406
。
406:表示客戶端無法解析服務端返回的內容。說白了就是後臺的返回結果前臺無法解析就報406錯誤。
哦,原來是忘了在Startup
中配置我們的孫子XmlDataContractSerializerOutputFormatter
。
services.AddControllers((c) =>
{
c.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
注意:不只是沒有在Startup
中會出現406
哦,以下情況也會出現:
- contentType不存在
- contentType與響應引數不匹配
OutputFormatter擴充套件
上面介紹了內建OutputFormatter
的使用,那如果我們想自定義呢?當然也是可以的。
下面我們就用自定義的OutputFormatter
實現頂部響應模板的效果:
public class ObjectOutputFormatter : TextOutputFormatter
{
public ObjectOutputFormatter()
{
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
// 這就是我們自定義contentType的名稱
SupportedMediaTypes.Add("text/object");
}
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (selectedEncoding == null)
{
throw new ArgumentNullException(nameof(selectedEncoding));
}
string text = JsonConvert.SerializeObject(new ResponseDto()
{
msg = "成功,自定義的哦",
code = 200,
data = context.Object
});
var response = context.HttpContext.Response;
await response.WriteAsync(text, selectedEncoding);
}
}
[Produces("text/object")]
[HttpGet]
public WeatherForecast Get()
{
return new WeatherForecast() { Date = DateTime.Now };
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers((c) =>
{
c.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
// 我們自定義的輸出格式
c.OutputFormatters.Add(new ObjectOutputFormatter());
});
}
搞定,我們來看看效果:
ActionFilterAttribute
有些同學可能會想到過濾器,是的,上面的效果過濾器也能實現:
public class ResultFilter : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
ResponseDto result = new ResponseDto();
result.code = 200;
result.msg = "成功,ResultFilter";
var properties = context.Result.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
result.data = properties.FirstOrDefault(c => c.Name == "Value").GetValue(context.Result);
context.Result = new JsonResult(result);
base.OnResultExecuting(context);
}
}
[TypeFilter(typeof(ResultFilter))]
[HttpGet]
public WeatherForecast Get()
{
return new WeatherForecast() { Date = DateTime.Now };
}
猴:有了過濾器為什麼還搞個OutputFormatter
呢?
我:不能因為過濾器可以實現同樣的功能就認為OutputFormatter
多餘了,很顯然過濾器的操作物件是請求/響應上下文,而OutputFormatter
的操作物件則是響應引數。再說了,ActionFilterAttribute
過濾器只是眾多過濾器的一種。
猴:那過濾器和自定義OutputFormatter
一起用會是什麼效果呢?是不是像下面這樣?
我:不是,過濾器和自定義OutputFormatter
同時使用,生效的只有過濾器,不信可以打斷點試一下哦。
[Produces("text/object")]
[TypeFilter(typeof(ResultFilter))]
[HttpGet]
public WeatherForecast Get()
{
return new WeatherForecast() { Date = DateTime.Now };
}
具體原因在這裡就不細說了,等後面再分享(其實我也還沒弄清楚,逼著自己去了解)
好了,這期的寶藏API
就到這了,下期再見哦,如果有下期的話。