事件
Power BI DataViz World Championships
2月14日 下午4時 - 3月31日 下午4時
4 次參賽機會,有機會贏得會議套裝行程,現場參與在拉斯維加斯舉行的總決賽
進一步了解ASP.NET Core MVC 支援 Web API 中使用輸入和輸出格式器的資料交換。 模型繫結會使用輸入格式器。 輸出格式器用來格式化回應。
此架構提供 JSON 和 XML 的內建輸入和輸出格式器。 其提供純文字的內建輸出格式器,但未提供純文字的輸入格式器。
本文說明如何藉由建立自訂的格式器來新增對其他格式的支援。 如需自訂純文字輸入格式器範例,請參閱 GitHub 上的 TextPlainInputFormatter。
檢視或下載範例程式碼 \(英文\) (如何下載)
使用自訂格式器,以新增內建格式器所未處理內容類型的支援。
建立自訂格式器:
若要建立格式器:
下列程式碼顯示範例中的 VcardOutputFormatter
類別:
public class VcardOutputFormatter : TextOutputFormatter
{
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(Type? type)
=> typeof(Contact).IsAssignableFrom(type)
|| typeof(IEnumerable<Contact>).IsAssignableFrom(type);
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object!, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
}
針對文字媒體類型 (例如 vCard),衍生自 TextInputFormatter 或 TextOutputFormatter 基底類別:
public class VcardOutputFormatter : TextOutputFormatter
針對二進位類型,衍生自 InputFormatter 或 OutputFormatter 基底類別。
在建構函式中,新增至 SupportedMediaTypes 和 SupportedEncodings 集合,即可指定所支援的媒體類型和編碼:
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
格式器類別「無法」對其相依性使用建構函式插入。 例如,ILogger<VcardOutputFormatter>
無法新增為建構函式的參數。 若要存取服務,請使用可傳入方法的內容物件。 本文中的程式碼範例和範例顯示如何執行此作業。
覆寫 CanReadType 或 CanWriteType 方法,即可指定要序列化或還原序列化的類型。 例如,透過 Contact
類型建立 vCard 文字,反之亦然:
protected override bool CanWriteType(Type? type)
=> typeof(Contact).IsAssignableFrom(type)
|| typeof(IEnumerable<Contact>).IsAssignableFrom(type);
在某些情況下,必須覆寫 CanWriteResult,而不是 CanWriteType。 如果符合下列所有條件,請使用 CanWriteResult
:
例如,假設動作方法:
Person
類型。Person
的 Student
或 Instructor
類型。針對只處理 Student
物件的格式器,請檢查您提供給 CanWriteResult
方法之內容物件中的 Object 類型。 動作方法傳回 IActionResult 時:
CanWriteResult
。CanWriteType
方法會接收執行階段類型。還原序列化或序列化是在 ReadRequestBodyAsync 或 WriteResponseBodyAsync 中執行。 下列範例顯示如何從相依性插入容器中取得服務。 無法從建構函式參數取得服務:
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object!, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
若要使用自訂格式器,請將格式器類別的執行個體新增至 MvcOptions.InputFormatters 或 MvcOptions.OutputFormatters 集合:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
格式器會依其插入順序進行評估,而且第一個格式器優先。
下列程式碼顯示範例中的 VcardInputFormatter
類別:
public class VcardInputFormatter : TextInputFormatter
{
public VcardInputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanReadType(Type type)
=> type == typeof(Contact);
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context, Encoding effectiveEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardInputFormatter>>();
using var reader = new StreamReader(httpContext.Request.Body, effectiveEncoding);
string? nameLine = null;
try
{
await ReadLineAsync("BEGIN:VCARD", reader, context, logger);
await ReadLineAsync("VERSION:", reader, context, logger);
nameLine = await ReadLineAsync("N:", reader, context, logger);
var split = nameLine.Split(";".ToCharArray());
var contact = new Contact(FirstName: split[1], LastName: split[0].Substring(2));
await ReadLineAsync("FN:", reader, context, logger);
await ReadLineAsync("END:VCARD", reader, context, logger);
logger.LogInformation("nameLine = {nameLine}", nameLine);
return await InputFormatterResult.SuccessAsync(contact);
}
catch
{
logger.LogError("Read failed: nameLine = {nameLine}", nameLine);
return await InputFormatterResult.FailureAsync();
}
}
private static async Task<string> ReadLineAsync(
string expectedText, StreamReader reader, InputFormatterContext context,
ILogger logger)
{
var line = await reader.ReadLineAsync();
if (line is null || !line.StartsWith(expectedText))
{
var errorMessage = $"Looked for '{expectedText}' and got '{line}'";
context.ModelState.TryAddModelError(context.ModelName, errorMessage);
logger.LogError(errorMessage);
throw new Exception(errorMessage);
}
return line;
}
}
執行本文的範例應用程式,其會實作基本 vCard 輸入和輸出格式器。 應用程式會讀取和寫入與下列格式類似的 vCard:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
若要查看 vCard 輸出,請執行應用程式,並將具有 Accept 標頭 text/vcard
的 Get 要求傳送至 https://localhost:<port>/api/contacts
。
將 vCard 新增至連絡人的記憶體內部集合:
Post
要求傳送至 /api/contacts
。Content-Type
標頭設定為 text/vcard
。vCard
文字,且格式類似上述範例。ASP.NET Core MVC 支援 Web API 中使用輸入和輸出格式器的資料交換。 模型繫結會使用輸入格式器。 輸出格式器用來格式化回應。
此架構提供 JSON 和 XML 的內建輸入和輸出格式器。 其提供純文字的內建輸出格式器,但未提供純文字的輸入格式器。
本文說明如何藉由建立自訂的格式器來新增對其他格式的支援。 如需自訂純文字輸入格式器範例,請參閱 GitHub 上的 TextPlainInputFormatter。
檢視或下載範例程式碼 \(英文\) (如何下載)
使用自訂格式器,以新增內建格式器所未處理內容類型的支援。
建立自訂格式器:
若要建立格式器:
下列程式碼顯示範例中的 VcardOutputFormatter
類別:
public class VcardOutputFormatter : TextOutputFormatter
{
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(Type type)
{
return typeof(Contact).IsAssignableFrom(type) ||
typeof(IEnumerable<Contact>).IsAssignableFrom(type);
}
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
}
針對文字媒體類型 (例如 vCard),衍生自 TextInputFormatter 或 TextOutputFormatter 基底類別:
public class VcardOutputFormatter : TextOutputFormatter
針對二進位類型,衍生自 InputFormatter 或 OutputFormatter 基底類別。
在建構函式中,新增至 SupportedMediaTypes 和 SupportedEncodings 集合,即可指定所支援的媒體類型和編碼:
public VcardOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
格式器類別「無法」對其相依性使用建構函式插入。 例如,ILogger<VcardOutputFormatter>
無法新增為建構函式的參數。 若要存取服務,請使用可傳入方法的內容物件。 本文中的程式碼範例和範例顯示如何執行此作業。
覆寫 CanReadType 或 CanWriteType 方法,即可指定要序列化或還原序列化的類型。 例如,透過 Contact
類型建立 vCard 文字,反之亦然:
protected override bool CanWriteType(Type type)
{
return typeof(Contact).IsAssignableFrom(type) ||
typeof(IEnumerable<Contact>).IsAssignableFrom(type);
}
在某些情況下,必須覆寫 CanWriteResult,而不是 CanWriteType。 如果符合下列所有條件,請使用 CanWriteResult
:
例如,假設動作方法:
Person
類型。Person
的 Student
或 Instructor
類型。針對只處理 Student
物件的格式器,請檢查您提供給 CanWriteResult
方法之內容物件中的 Object 類型。 動作方法傳回 IActionResult 時:
CanWriteResult
。CanWriteType
方法會接收執行階段類型。還原序列化或序列化是在 ReadRequestBodyAsync 或 WriteResponseBodyAsync 中執行。 下列範例顯示如何從相依性插入容器中取得服務。 無法從建構函式參數取得服務:
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardOutputFormatter>>();
var buffer = new StringBuilder();
if (context.Object is IEnumerable<Contact> contacts)
{
foreach (var contact in contacts)
{
FormatVcard(buffer, contact, logger);
}
}
else
{
FormatVcard(buffer, (Contact)context.Object, logger);
}
await httpContext.Response.WriteAsync(buffer.ToString(), selectedEncoding);
}
private static void FormatVcard(
StringBuilder buffer, Contact contact, ILogger logger)
{
buffer.AppendLine("BEGIN:VCARD");
buffer.AppendLine("VERSION:2.1");
buffer.AppendLine($"N:{contact.LastName};{contact.FirstName}");
buffer.AppendLine($"FN:{contact.FirstName} {contact.LastName}");
buffer.AppendLine($"UID:{contact.Id}");
buffer.AppendLine("END:VCARD");
logger.LogInformation("Writing {FirstName} {LastName}",
contact.FirstName, contact.LastName);
}
若要使用自訂格式器,請將格式器類別的執行個體新增至 MvcOptions.InputFormatters 或 MvcOptions.OutputFormatters 集合:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
options.OutputFormatters.Insert(0, new VcardOutputFormatter());
});
}
系統會依據您插入格式器的順序進行評估。 第一個會優先使用。
下列程式碼顯示範例中的 VcardInputFormatter
類別:
public class VcardInputFormatter : TextInputFormatter
{
public VcardInputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanReadType(Type type)
{
return type == typeof(Contact);
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context, Encoding effectiveEncoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<VcardInputFormatter>>();
using var reader = new StreamReader(httpContext.Request.Body, effectiveEncoding);
string nameLine = null;
try
{
await ReadLineAsync("BEGIN:VCARD", reader, context, logger);
await ReadLineAsync("VERSION:", reader, context, logger);
nameLine = await ReadLineAsync("N:", reader, context, logger);
var split = nameLine.Split(";".ToCharArray());
var contact = new Contact
{
LastName = split[0].Substring(2),
FirstName = split[1]
};
await ReadLineAsync("FN:", reader, context, logger);
await ReadLineAsync("END:VCARD", reader, context, logger);
logger.LogInformation("nameLine = {nameLine}", nameLine);
return await InputFormatterResult.SuccessAsync(contact);
}
catch
{
logger.LogError("Read failed: nameLine = {nameLine}", nameLine);
return await InputFormatterResult.FailureAsync();
}
}
private static async Task<string> ReadLineAsync(
string expectedText, StreamReader reader, InputFormatterContext context,
ILogger logger)
{
var line = await reader.ReadLineAsync();
if (!line.StartsWith(expectedText))
{
var errorMessage = $"Looked for '{expectedText}' and got '{line}'";
context.ModelState.TryAddModelError(context.ModelName, errorMessage);
logger.LogError(errorMessage);
throw new Exception(errorMessage);
}
return line;
}
}
執行本文的範例應用程式,其會實作基本 vCard 輸入和輸出格式器。 應用程式會讀取和寫入與下列格式類似的 vCard:
BEGIN:VCARD
VERSION:2.1
N:Davolio;Nancy
FN:Nancy Davolio
END:VCARD
若要查看 vCard 輸出,請執行應用程式,並將具有 Accept 標頭 text/vcard
的 Get 要求傳送至 https://localhost:5001/api/contacts
。
將 vCard 新增至連絡人的記憶體內部集合:
Post
要求傳送至 /api/contacts
。Content-Type
標頭設定為 text/vcard
。vCard
文字,且格式類似上述範例。事件
Power BI DataViz World Championships
2月14日 下午4時 - 3月31日 下午4時
4 次參賽機會,有機會贏得會議套裝行程,現場參與在拉斯維加斯舉行的總決賽
進一步了解訓練
模組
使用 ASP.NET Core 控制器建立 Web API - Training
使用 ASP.NET Core 控制器建立一個支援建立、讀取、更新和刪除 (CRUD) 作業的 RESTful 服務。
文件
深入了解 ASP.NET Core 中的 Web API 慣例。
了解 ASP.NET Core MVC Web API 分析器套件。
ASP.NET Core Web API 中的 JsonPatch
了解如何處理 ASP.NET Core Web API 中的 JSON Patch 要求。