Asp.Net Core 3.0 學習3、Web Api 檔案上傳 Ajax請求以及跨域問題
1、建立Api項目
我用的是VS2019 Core3.1 。打開Vs2019 建立Asp.Net Core Web應用程式命名CoreWebApi 建立選擇API 在Controller檔案夾下面添加一個Api控制器 FileUp,修改Api的路由 [Route("api/[controller]/[action]")] 這樣就可以通路到具體的某一個了 寫一個測試 api
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace CoreWebApi.Controllers
{
[Route("api/[controller]/[action]")]//修改路由
//[Route("api/[controller]")]//預設路由
[ApiController]
public class FileUpController : ControllerBase
{
/// <summary>
/// 測試接口
/// </summary>
/// <returns></returns>
[HttpGet]
//[HttpGet,Route("Test")]//預設路由
public string Test()
{
return "這是測試接口";
}
}
}
寫Api的時候一定要加上,請求方式 post、get 其他的暫時我沒用到。預設的路由是被我注釋的,修改一下 讓他的通路時api / 控制器 / 方法名稱。不改也可以,測試的接口就用注釋的那個特性。然後運作項目,位址欄輸入
https://localhost:44376/api/FileUp/Test2、上傳檔案接口
在FileUp裡寫一個上傳檔案的接口所有代碼都在下,OutPut是一個輸出的類,Dto裡面是一些參數 應該都有看得懂。裡面就是上傳檔案儲存到伺服器上 并沒有資料庫操作,需要的加上去就可以了。
注意:有些會加[Consumes("application/json")]//application/json//application/x-www-form-urlencoded 這樣來驗證媒體類型,這裡我弄了好久才想起來我寫了這個。因為檔案上傳怎麼說呢,媒體類型肯定不會是Json之類的。我以前的Api就是在基礎Api類裡加了這個。給我弄了頭皮發麻才看到
using CoreWebApi.Models;
using Newtonsoft.Json;
using Microsoft.Extensions.Hosting;
using System.IO;
[Route("api/[controller]/[action]")]//修改路由
//[Route("api/[controller]")]//預設路由
[ApiController]
public class FileUpController : ControllerBase
{
public IHostingEnvironment env;
public FileUpController(IHostingEnvironment _env)
{
env = _env;
}
/// <summary>
/// 測試接口
/// </summary>
/// <returns></returns>
[HttpGet]
//[HttpGet,Route("Test")]//預設路由
public string Test()
{
return "這是測試接口";
}
/// <summary>
/// 檔案上傳
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<OutPut> FileUp()
{
var ret = new OutPut();
try
{
//不能用FromBody
var dto = JsonConvert.DeserializeObject<ImagesDto>(Request.Form["ImageModelInfo"]);//檔案類實體參數
var files = Request.Form.Files;//接收上傳的檔案,可能多個 看前台
if (files.Count > 0)
{
var path = env.ContentRootPath + @"/Uploads/Images/";//絕對路徑
string dirPath = Path.Combine(path, dto.Type + "/");//絕對徑路 儲存檔案路徑的檔案夾
if (!Directory.Exists(dirPath))//檢視檔案夾是否存在
Directory.CreateDirectory(dirPath);
var file = files.Where(x => true).FirstOrDefault();//隻取多檔案的一個
var fileNam = $"{Guid.NewGuid():N}_{file.FileName}";//新檔案名
string snPath = $"{dirPath + fileNam}";//儲存檔案路徑
using var stream = new FileStream(snPath, FileMode.Create);
await file.CopyToAsync(stream);
//次出還可以進行資料庫操作 儲存到資料庫
ret = new OutPut { Code = 200, Msg = "上傳成功", Success = true };
}
else//沒有圖檔
{
ret = new OutPut { Code = 400, Msg = "請上傳圖檔", Success = false };
}
}
catch (Exception ex)
{
ret = new OutPut { Code = 500, Msg = $"異常:{ex.Message}", Success = false };
}
return ret;
}
}
Dto與傳回的類
///
/// 傳回輸出類
/// </summary>
public class OutPut
{
/// <summary>
/// 狀态碼
/// </summary>
public int Code { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 是否成功
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 傳回資料
/// </summary>
public object Data { get; set; }
}
/// 接收參數Dto
/// </summary>
public class ImagesDto
{
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }
/// <summary>
/// 名稱
/// </summary>
public string Name { get; set; }
/// <summary>
/// 位址
/// </summary>
public string Url { get; set; }
/// <summary>
/// 備注
/// </summary>
public string Remark { get; set; }
/// <summary>
///
/// </summary>
public int RelationId { get; set; }
/// <summary>
/// 類型
/// </summary>
public int Type { get; set; }
}
3、前台Ajax調用
我在一個MVC程式裡面用了個Ajax調用 下面是前端代碼,contentType這個資源的類型我轉載着坑裡好久了,哈哈。
@{
ViewBag.Title = "測試";
檔案上傳測試
<div>
<br /><br /><br />
檔案:<input type="file" id="filesp" name="filesp" /><br /><br />
名稱:<input type="text" id="fileName" name="fileName" /><br /><br />
備注:<input type="text" id="txtRemake" name="txtRemake" /><br /><br />
RelationId:<input type="number" id="txtRelationId" name="txtRelationId" /><br /><br />
類型:<select id="selType" name="selType">
<option value="1">動物</option>
<option value="2">植物</option>
<option value="3">妹子</option>
<option value="3">風景</option>
<option value="4">滑稽</option>
<option value="100">其他</option>
</select>
<br /><br />
<input type="button" id="btnSave" name="btnSave" value="送出" />
</div>
$(document).ready(function () {
$("#btnSave").click(function () {
var data = new FormData(document.getElementById("formData"));
//參數
var parame = JSON.stringify({ Name: $("#fileName").val(), Remark: $("#txtRemake").val(), Type: $("#selType").val(), RelationId: $("#txtRelationId").val() });
data.append("ImageModelInfo", parame);
$.ajax({
type: "post",
url: "https://localhost:44376/api/FileUp/FileUp",
dataType: "json",
data: data,
async: true,
contentType: false,//實體頭部用于訓示資源的MIME類型 media type 。這裡要為false
processData: false,//processData 預設為true,當設定為true的時候,jquery ajax 送出的時候不會序列化 data,而是直接使用data
success: function (data) {
console.log(data);
},
error: function (data) {
console.log("錯誤" + data);
}
});
});
});
運作起來看看 測試一下 ,Api那邊也打好斷點。打來浏覽器調式就是很熟悉的bug就來了(一天看不到出錯仿佛心裡不踏實)
很明顯的CORS是一個W3C标準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許浏覽器向跨源伺服器,發出XMLHttpRequest請求,進而克服了AJAX隻能同源使用的限制。是以這裡我們就要允許前端來通路,進而就要在Api增加跨域;
4、跨域設定
在Startup.cs下面的ConfigureServices 中标添加services.AddCors(option => option.AddPolicy("AllowCors", bu => bu.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//跨域
services.AddCors(option => option.AddPolicy("AllowCors", bu => bu.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
}
AllowAnyOrigin :允許CORS請求從任何源來通路,這是不安全的
AllowAnyHeader:允許所有的請求頭
AllowCredentials :服務端也需要允許證書。
AllowAnyMethod允許跨域政策允許所有的方法:GET/POST/PUT/DELETE 等方法 如果進行限制需要 AllowAnyMethod("GET","POST") 這樣來進行通路方法的限制
在Configure中添加中間件app.UseCors("AllowCors");,集體要限制哪些看個人了
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
//跨域
app.UseCors("AllowCors");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
現在就配置好了在需要跨域的地方加上特性[EnableCors("AllowCors")]就行了 ,引用命名空間using Microsoft.AspNetCore.Cors;
[EnableCors("AllowCors")]
[Route("api/[controller]/[action]")]//修改路由
//[Route("api/[controller]")]//預設路由
[ApiController]
public class FileUpController : ControllerBase
{
public IHostingEnvironment env;
public FileUpController(IHostingEnvironment _env)
{
env = _env;
}
/// <summary>
/// 測試接口
/// </summary>
/// <returns></returns>
[HttpGet]
//[HttpGet,Route("Test")]//預設路由
public string Test()
{
return "這是測試接口";
}
現在再來調用就解決了
前台結果看看。檔案夾也建立了。
5、總結
如果所有Api都要跨域的話就建立一個基礎的Api 讓所有的都繼承這個Api,就需要打賞跨域的标簽就可以了;
檔案上傳還可以用From直接送出這邊用List接收,也可以像FromBady一樣的一個來接收搞,單詞 忘了0.0,參數不能FermBody接收,這樣檔案就沒了。
紙上得來終覺淺,覺知這事不寫還是不行哈。
原文位址
https://www.cnblogs.com/w5942066/p/12762076.html