ASP.NET Core开发常见问题

ASP.NET Core中返回 json 数据首字母大小写问题

在asp.net core中使用ajax请求动态绑定数据时遇到问题:

后台返回数据字段首字母为定义的大写,返回的数据没有问题;但是在前台得到的数据,字段首字母却变成了小写

此时用定义的首字母大写字段去接收数据会显示undefined,这是因为在asp.net core中json序列化默认使用驼峰格式处理字段,首字母变成小写,所以获取不到数据。

解决方法:在Startup类的ConfigureServices()方法中进行配置,DefaultContractResolver() 原样输出,返回的 json 与后台定义一致

 public void ConfigureServices(IServiceCollection services)
        { 
            ...
            services.AddMvc().AddJsonOptions(opt =>
                     {
                         opt.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();//json字符串大小写原样输出
                     });
        }

此时json字符串字段首字母大小与后台定义一致

参考:ASP.NET Core中返回 json 数据首字母大小写问题

WebAPI——修改默认监听端口

添加host.json文件

{
  "urls": "https://localhost:44389;http://localhost:44380"
}

在Program.cs中修改CreateWebHostBuilder方法

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
   {
            var configuration = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory)
                                          .AddJsonFile("host.json")
                                          .Build();

            var url = configuration["urls"];

            return WebHost.CreateDefaultBuilder(args)
                   .UseUrls(url)
                   .UseStartup<Startup>();
   }

参考官网:ASP.NET Core Web 主机

HttpClient Post json参数请求

被请求的接口代码如下:

//被请求接口
    // POST api/values
    [HttpPost]
    public MyProperty Post([FromBody] MyProperty value)
    {
        return value;
    }
    
    //参数
    public class MyProperty
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

注:若提交报错:通过post提交Json数据时,接口返回,"{\"\":[\"Unexpected character encountered while parsing value: {. Path '', line 1, position 1.\"]}"

则可能是因为参数 写成了string类型([FromBody] string value)

使用HttpClient的PostAsync方法发送Json数据请求

public class JsonContent : StringContent
    {
        public JsonContent(object obj) :
           base(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json")
        { }
    }


    public static string HttpClientPost(string url, object datajson)
    {
        HttpClient httpClient = new HttpClient();//http对象
                                                 //表头参数
        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        //转为链接需要的格式
        HttpContent httpContent = new JsonContent(datajson);
        //请求
        HttpResponseMessage response = httpClient.PostAsync(url, httpContent).Result;
        if (response.IsSuccessStatusCode)
        {
            Task<string> t = response.Content.ReadAsStringAsync();
            if (t != null)
            {
                return t.Result;
            }
        }
        return "";
    }

调用

//调用
    MyProperty myProperty = new MyProperty();
    myProperty.ID = 666;
    myProperty.Name = "Tom猫";
 
    var result = HttpClientPost("http://localhost:59958/api/values", myProperty);
    Console.WriteLine(result);

wwwroot目录下文件访问不了

 private IHostingEnvironment _host;
        public NewsController(IHostingEnvironment host)
        {
            this._host = host;
        }

string webRootPath = _host.WebRootPath;  //获取的即是wwwroot

net core 3.1中 推荐用IHostEnvironment,路径获取用:Path.Combine(_host.ContentRootPath, "wwwroot") 

方案:允许站点不识别content-type下载文件(即:不受mime类型限制下载)

对于netcore的web项目而言,内置了一些content-type允许下载的文件类型,eg,常见的image、pdf、zip等,有些却未运行,eg:nupkg、apk,

要想.apk,.nupkg.cs等后缀的文件不被限制,我们可以:

 app.UseStaticFiles(new StaticFileOptions
            {
                //设置不限制content-type
                ServeUnknownFileTypes = true 
            });

上面例子我们能够使用 ServeUnknownFileTypes = true; 直接设置无限制下载文件类型,这种通常不是太好或者说不允许,亦或者不常说的不安全吧;如果我们只需要增加.nupkg和.apk后缀的文件的下载,那么可以通过如下代码来添加mime类型,如:

app.UseStaticFiles(new StaticFileOptions
            {
                //ServeUnknownFileTypes = true 
                ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>
                {
                    { ".apk","application/vnd.android.package-archive"},
                    { ".nupkg","application/zip"}
                })
            });

再比如要下载一个byte[]写入的 xx.tsa二进制文件,可以加一个:{ ".tsa", "application/octet-stream" }

参考:

StaticFiles 之 FileExtensionContentTypeProvider

MIME 参考手册

文件下载

1、后端直接返回 文件的地址,前端定位,直接下载

后端:

ViewBag.zipFile = zipFile ;  // zipFile文件在服务器上的绝对路径 

html:

<el-button type="primary" @@click="onDownTSA('@ViewBag.zipFile')">下载文件(.zip)</el-button>
onDownTSA(zipFile) {
            window.open(zipFile, "_blank")
        }

2、后端返回文件对象,前端请求下载

  • 下载完不需要返回值
onDownTSA(zipName) {
downloadFiles("/ApplyTimeStamp/DownTsaFile", { zipName: zipName });
}

//无返回值
function downloadFiles(url, params) {
    try {
        var form = document.createElement("form");
        form.id = "downloadForm";
        form.style.display = "none";
        form.target = "";
        form.method = "post";
        form.action = url;
        for (var key in params) {
            var input = document.createElement("input");
            input.type = "hidden";
            input.name = key;
            input.value = params[key];
            form.appendChild(input);
        }
        document.body.appendChild(form);
        form.submit();
        document.body.removeChild(form);
    } catch (ex) {
        Vue.prototype.$message.error(ex);
    }
}

后端:

[Description("下载证明文件")]
        public async Task<IActionResult> DownTsaFile(string zipName)
        {
            try
            {
                byte[] bytes = null;
                zipName = zipName.Trim();
                string path = Path.Combine("wwwroot", WebConstant.TsaFileDirName, zipName);
                if (System.IO.File.Exists(path))
                {
                    bytes = await System.IO.File.ReadAllBytesAsync(path);
                }
                return File(bytes, "application/octet-stream", zipName);
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex.Message, ex);
                ObjectResult objectResult = new ObjectResult(new ResultDTO());
                return objectResult; //没什么用处
            }
        }
  • 下载完后需要返回值

使用 Blob 构造函数可以直接在客户端上创建和操作 Blob(通常等效于一个文件)。

Internet Explorer 10 的 msSaveBlob 和 msSaveOrOpenBlob 方法允许用户在客户端上保存文件,方法如同从 Internet 下载文件,这是此类文件保存到“下载”文件夹的原因。

msSaveBlob:只提供一个保存按钮
msSaveOrOpenBlob:提供保存和打开按钮
onDownTSA(zipName) {
            downloadFiles("/ApplyTimeStamp/DownTsaFile", { zipName: zipName });
        }

//有返回值
function downloadFiles(url, params) {
    try {
        var form = document.createElement("form");
        form.id = "downloadForm";
        form.style.display = "none";
        form.target = "";
        form.method = "post";
        let filename = "";
        for (var key in params) {
            var input = document.createElement("input");
            input.type = "hidden";
            input.name = key;
            input.value = params[key];
            filename = input.value;
            form.appendChild(input);
        }
        document.body.appendChild(form);
       
        let dataN = new FormData(form);
        axios.post(url, dataN, { responseType: 'blob' }).then(response => {
            var data = response.data;
            if (response.status == 200) {  //请求成功
                if (window.navigator.msSaveOrOpenBlob) {
                    let blob = new Blob([data])
                    navigator.msSaveBlob(blob, filename);
                } else {
                    let blob = new Blob([data])
                    var link = document.createElement('a');
                    link.href = window.URL.createObjectURL(blob);
                    link.download = filename;
                    link.click();
                    window.URL.revokeObjectURL(link.href);
                }

                Vue.prototype.$message({
                    message: '下载成功',
                    type: 'success'
                });
                document.body.removeChild(form);
                url = "/ApplyTimeStamp/Index";
                location.href = url;
            } 
        }).catch((ex) => {  //请求失败
            if (ex.response.status == 404) {
                Vue.prototype.$message.error('文件不存在或者已被下载!');
            } else if (ex.response.status == 400) {
                Vue.prototype.$message.error('参数异常:文件名为空异常!'); 
            }
        });
    } catch (ex) {
        Vue.prototype.$message.error(ex);
    }
}
View Code

后端:

 [Description("下载证明文件")]
        public async Task<IActionResult> DownTsaFile(string zipName)
        {
            var error = new ResultDTO(0, "");
            try
            {
                if (!string.IsNullOrEmpty(zipName))
                {
                    byte[] bytes = null;
                    zipName = zipName.Trim();
                    string path = Path.Combine("wwwroot", WebConstant.TsaFileDirName, zipName);
                    if (System.IO.File.Exists(path))
                    {
                        bytes = await System.IO.File.ReadAllBytesAsync(path);
                    }
                    else
                    {
                        throw new Exception($"文件不存在或者已被下载");
                    }
                    if (System.IO.File.Exists(path))
                    {
                        System.IO.File.Delete(path);
                    }
                    Response.StatusCode = (int)HttpStatusCode.OK;
                    //HttpUtility.UrlEncode(zipName,Encoding.UTF8)
                    return File(bytes, "application/octet-stream", zipName);
                }
                else
                {
                    error.Code = -1;
                    error.Msg = $"参数异常:文件名为空异常";
                    ObjectResult objectResult = new ObjectResult(error);
                    Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    return objectResult;
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex.Message, ex);
                error.Code = -1;
                error.Msg = $"下载文件异常:{ex.Message}";
                ObjectResult objectResult = new ObjectResult(error);
                Response.StatusCode = (int)HttpStatusCode.NotFound;
                return objectResult; //没什么用处
            }
        }
View Code

参考:

jQuery 利用其 AJAX 下载后端返回的 application/octet-stream 二进制数据

Vue responseType中blob和arrayBuffer下载文件以及JSON处理

 

posted @ 2020-01-30 17:22  peterYong  阅读(1460)  评论(0编辑  收藏  举报