探索 ASP.NET WebAPI (.NET Framework)
2023-02-23
探索 ASP.NET WebAPI 加入專案,使用 Northwind Database 處理的「不需要資料合約名稱」問題以及常見的 Json 使用 camelCase 回應的設定方式。
說明
Global.asax
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
在 Global.asax
加入 WebApi 的註冊,要注意的是註冊必須在 Routes 的註冊之前完成。
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
// GlobalConfiguration.Configuration.Formatters.Add(new XmlMediaTypeFormatter());
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
使用 Formatters.Clear
可以清除預設的 Formatters (包含 Json 以及 XML),而重新加入 Json 後,就不會因為所收到的 Header 不同,提供不同的回應,固定只會以 Json 進行回應。
此外藉由 JsonFormatter.SerializerSettings
可以設定回應的縮排以及命名方式 (相較於 .NET 的 PascalCase Json 的世界喜歡用 camelCase)。
Trouble Shooting
使用 Northwind Database 的 Customer 當作 WebAPI 的回應內容會發生以下錯誤:
<ExceptionMessage>不需要資料合約名稱為 'Customer_D5F79FA43AD7E38F57F7895655DF9753781705EBD3ED0A7DE5C4FF20C5DE9F7F: http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' 的類型 'System.Data.Entity.DynamicProxies.Customer_D5F79FA43AD7E38F57F7895655DF9753781705EBD3ED0A7DE5C4FF20C5DE9F7F'。如果您使用 DataContractSerializer,請考慮使用 DataContractResolver,或將任何統計上不明的類型加入已知類型清單 - 例如,使用 KnownTypeAttribute 屬性,或將它們加入已傳送給序列化程式的已知類型清單。</ExceptionMessage>
參考 ASP.NET MVC 4 WebApi 使用 Northwind 建置 Controller 所遇到的問題 的說明,只要使用 db.Configuration.ProxyCreationEnabled = false;
即可修正。
反覆在每個 Action 當中使用很麻煩,可以在 DbContext 或者是繼承的 Controller 來達到全域設定。
[ResponseType(typeof(Customer))]
public async Task<IHttpActionResult> GetCustomer(string id)
{
db.Configuration.ProxyCreationEnabled = false;
Customer customer = await db.Customers.FindAsync(id);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
Scaffolding
public IQueryable<Customer> GetCustomers()
{
db.Configuration.ProxyCreationEnabled = false;
return db.Customers;
}
// GET: api/CustomerInfo/BERGS
[ResponseType(typeof(Customer))]
public async Task<IHttpActionResult> GetCustomer(string id)
{
db.Configuration.ProxyCreationEnabled = false;
Customer customer = await db.Customers.FindAsync(id);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
// PUT: api/CustomerInfo/BERGS
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutCustomer(string id, Customer customer)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != customer.CustomerID)
{
return BadRequest();
}
db.Entry(customer).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/CustomerInfo
[ResponseType(typeof(Customer))]
public async Task<IHttpActionResult> PostCustomer(Customer customer)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Customers.Add(customer);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (CustomerExists(customer.CustomerID))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = customer.CustomerID }, customer);
}
// DELETE: api/CustomerInfo/BERGS
[ResponseType(typeof(Customer))]
public async Task<IHttpActionResult> DeleteCustomer(string id)
{
Customer customer = await db.Customers.FindAsync(id);
if (customer == null)
{
return NotFound();
}
db.Customers.Remove(customer);
await db.SaveChangesAsync();
return Ok(customer);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool CustomerExists(string id)
{
return db.Customers.Count(e => e.CustomerID == id) > 0;
}