10

.NET宝藏API之:OutputFormatter,格式化输出对象 - 畅饮无绪

 3 years ago
source link: https://www.cnblogs.com/cool-net/p/16176643.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

.NET宝藏API之:OutputFormatter,格式化输出对象

相信大家在项目中都用过统一响应参数模板。

先声明一个响应模板类:

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);
}
image

当然了,这篇文章所讲的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 };
}

我们来运行看一看:

image

wtf,怎么会406

406:表示客户端无法解析服务端返回的内容。说白了就是后台的返回结果前台无法解析就报406错误。

哦,原来是忘了在Startup中配置我们的孙子XmlDataContractSerializerOutputFormatter

services.AddControllers((c) =>
{
	c.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
image

注意:不只是没有在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());
	});
}

搞定,我们来看看效果:

image

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 };
}
image

猴:有了过滤器为什么还搞个OutputFormatter呢?

我:不能因为过滤器可以实现同样的功能就认为OutputFormatter多余了,很显然过滤器的操作对象是请求/响应上下文,而OutputFormatter的操作对象则是响应参数。再说了,ActionFilterAttribute过滤器只是众多过滤器的一种。

猴:那过滤器和自定义OutputFormatter一起用会是什么效果呢?是不是像下面这样?

image

我:不是,过滤器和自定义OutputFormatter同时使用,生效的只有过滤器,不信可以打断点试一下哦。

[Produces("text/object")]
[TypeFilter(typeof(ResultFilter))]
[HttpGet]
public WeatherForecast Get()
{
	return new WeatherForecast() { Date = DateTime.Now };
}
image

具体原因在这里就不细说了,等后面再分享(其实我也还没弄清楚,逼着自己去了解)

好了,这期的宝藏API就到这了,下期再见哦,如果有下期的话。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK