前文索引:
ASP.NET Core教程【二】從保存數據看Razor Page的特有屬性與服務端驗證
ASP.NET Core教程【一】關於Razor Page的知識
實體字段屬性
再來看看咱們的實體類
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
說明,上面的代碼須要引用:using System.ComponentModel.DataAnnotations;
Display屬性標誌這個字段在頁面上顯示的時候,須要顯示什麼名字;
咱們在上一篇文章中用到的:
<label asp-for="Movie.Title" class="control-label"></label>
這裏就會顯示Display屬性指定的名字;
DataType屬性標誌這個字段是什麼類型的;
上一章中咱們說到的,數據類型的驗證工做,就是依據這裏標誌的數據類型來完成的
好比你能夠增長以下數據約束
[StringLength(60, MinimumLength = 3)]
[Required]
[Range(1, 100)]
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
[DataType(DataType.Currency)]
若是你想格式化輸出的內容,你可使用以下的屬性註釋
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}"]
你能夠在同一行代碼中標記多個屬性,以下:
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$"), Required, StringLength(30)]
更多說明文檔,能夠查閱:https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-6
連接標籤
在上一篇文章中咱們簡單說了一下連接標籤,再來看第一章中提到的這個場景:
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a>
<a asp-page="./Details" asp-route-id="@item.ID">Details</a>
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
asp-page和asp-route-id兩個屬性共同決定了最終編譯出來的連接地址;
之前咱們可能要拼字符串來構造href屬性,如今不用了,能夠分開寫,代碼更優雅;
上面的代碼,編譯後生成的href屬性的值是這樣的:
http://localhost:5000/Movies/Details?id=2
如今咱們分別打開Edit.cshtml、Details.cshtml、Delete.cshtml
把頁面中的第一個命令:@page,修改成:@page "{id:int}"
從新編譯運行,發現上面的連接變成了:
http://localhost:5000/Movies/Details/1
看到這裏你會說「呦~」嗎?😄
若是這個時候你請求這個地址:
http://localhost:5000/Movies/Details
並無傳入ID的值,那麼服務器會返回404,
若是你的設計是但願ID是一個可選的傳入參數,那麼你能夠把page指令修改爲:
若是你想讓頁面接收一個字符串,能夠把這個「路由模版」寫成以下這個樣子:
併發數據異常
當一個用戶刪除了一個實體,另外一個用戶同時又要更新這個實體的話
第二個用戶的請求就會拋出併發數據異常(這裏姑且稱做併發,微軟官網就是這麼說的),來看代碼:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!_context.Movie.Any(e => e.ID == Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
上面代碼中DbUpdateConcurrencyException就是專門針對這種異常定義的異常類;
NotFound方法將返回404異常
文件上傳及讀取
若是你想上傳一個文件,能夠撰寫以下razor page的代碼(只提供一部分表單域)
<div class="form-group">
<label asp-for="FileUpload.UploadPublicSchedule" class="control-label"></label>
<input asp-for="FileUpload.UploadPublicSchedule" type="file" class="form-control" style="height:auto" />
<span asp-validation-for="FileUpload.UploadPublicSchedule" class="text-danger"></span>
</div>
這個表單域對應的實體以下
using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations;
namespace RazorPagesMovie.Models
{
public class FileUpload
{
[Required]
[Display(Name="Title")]
[StringLength(60, MinimumLength = 3)]
public string Title { get; set; }
[Required]
[Display(Name="Public Schedule")]
public IFormFile UploadPublicSchedule { get; set; }
}
}
咱們只要關注第二個字段便可,UploadPublicSchedule是一個IFormFile類型的字段;
當表單提交後,ASP.NET CORE 也會把文件流綁定到這個字段上;
若是上傳的是一個文本文件,那麼咱們看看怎麼直接讀取這個文本文件;
public static async Task<string> ProcessFormFile(IFormFile formFile, ModelStateDictionary modelState)
{
var fieldDisplayName = string.Empty;
// 經過反射拿到實例的字段,再拿到字段的DisplayAttribute
MemberInfo property = typeof(FileUpload).GetProperty(formFile.Name.Substring(formFile.Name.IndexOf(".") + 1));
if (property != null)
{
var displayAttribute = property.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute;
if (displayAttribute != null)
{
fieldDisplayName = $"{displayAttribute.Name} ";
}
}
// 經過Path.GetFileName拿到文件名
var fileName = WebUtility.HtmlEncode(Path.GetFileName(formFile.FileName));
if (formFile.ContentType.ToLower() != "text/plain")
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) must be a text file.");
}
// 判斷文件長度
if (formFile.Length == 0)
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) is empty.");
}
else
{
try
{
string fileContents;
using (var reader = new StreamReader(formFile.OpenReadStream(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true))
{
fileContents = await reader.ReadToEndAsync();
// 再驗證一遍文件內容的長度,以防文件只有一個BOM頭
if (fileContents.Length > 0)
{
return fileContents;
}
else
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) is empty.");
}
}
}
catch (IOException ex)
{
modelState.AddModelError(formFile.Name, $"The {fieldDisplayName}file ({fileName}) upload failed. Please contact the Help Desk for support.");
}
}
return string.Empty;
}
調用上面方法的代碼以下:
var publicScheduleData = await FileHelpers.ProcessFormFile(FileUpload.UploadPublicSchedule, ModelState);
其中ModelState是PageModel特有的屬性
在本示例中,用於給頁面添加錯誤信息~