Smartstore.BTCPayServer/tools/Smartstore.WebApi.Client/HttpClient/_WebApiClient.cs
2022-09-29 16:10:00 +02:00

232 lines
8.5 KiB
C#

using System.Net;
using System.Text;
using Microsoft.AspNetCore.StaticFiles;
using Smartstore.WebApi.Client.Models;
namespace Smartstore.WebApi.Client
{
public class WebApiClient
{
public static string JsonAcceptType => "application/json";
public static bool BodySupported(string method)
=> !string.IsNullOrWhiteSpace(method) && string.Compare(method, "GET", true) != 0 && string.Compare(method, "DELETE", true) != 0;
// So far all API methods with multipart support are POST methods.
public static bool MultipartSupported(string method)
=> string.Compare(method, "POST", true) == 0;
public Task<HttpWebRequest> StartRequestAsync(
WebApiRequestContext context,
string content,
Dictionary<string, object> multipartData,
out StringBuilder requestContent)
{
throw new NotImplementedException();
}
public Task<bool> ProcessResponseAsync(
HttpWebRequest webRequest,
WebApiResponse response,
FolderBrowserDialog folderBrowserDialog)
{
throw new NotImplementedException();
}
public Dictionary<string, object> CreateMultipartData(FileUploadModel model)
{
if (!(model?.Files?.Any() ?? false))
{
return null;
}
var result = new Dictionary<string, object>();
var isValid = false;
var count = 0;
// Identify entity by its identifier.
if (model.Id != 0)
{
result.Add("Id", model.Id);
}
// Custom properties like SKU etc.
foreach (var kvp in model.CustomProperties)
{
if (kvp.Key.HasValue() && kvp.Value != null)
{
result.Add(kvp.Key, kvp.Value);
}
}
// File data.
foreach (var file in model.Files)
{
if (File.Exists(file.LocalPath))
{
using var fstream = new FileStream(file.LocalPath, FileMode.Open, FileAccess.Read);
byte[] data = new byte[fstream.Length];
fstream.Read(data, 0, data.Length);
var name = Path.GetFileName(file.LocalPath);
var id = $"my-file-{++count}";
new FileExtensionContentTypeProvider().TryGetContentType(name, out string contentType);
var apiFile = new ApiFileParameter(data, name, contentType);
// Add file parameters. Omit default values (let the server apply them).
if (file.Id != 0)
{
apiFile.Parameters.Add("PictureId", file.Id.ToString());
}
if (file.Path.HasValue())
{
apiFile.Parameters.Add("Path", file.Path);
}
if (!file.IsTransient)
{
apiFile.Parameters.Add("IsTransient", file.IsTransient.ToString());
}
if (file.DuplicateFileHandling != DuplicateFileHandling.ThrowError)
{
apiFile.Parameters.Add("DuplicateFileHandling", ((int)file.DuplicateFileHandling).ToString());
}
// Test pass through of custom parameters but the API ignores them anyway.
//apiFile.Parameters.Add("CustomValue1", string.Format("{0:N}", Guid.NewGuid()));
//apiFile.Parameters.Add("CustomValue2", string.Format("say hello to {0}", id));
result.Add(id, apiFile);
isValid = true;
fstream.Close();
}
}
if (!isValid)
{
return null;
}
return result;
}
private async Task GetResponse(HttpWebResponse webResponse, WebApiResponse response, FolderBrowserDialog dialog)
{
if (webResponse == null)
{
return;
}
response.Status = $"{(int)webResponse.StatusCode} {webResponse.StatusDescription}";
response.Headers = webResponse.Headers.ToString();
response.ContentType = webResponse.ContentType;
response.ContentLength = webResponse.ContentLength;
var ct = response.ContentType;
if (ct.HasValue() && (ct.StartsWith("image/") || ct.StartsWith("video/") || ct == "application/pdf"))
{
dialog.Description = "Please select a folder to save the file return by Web API.";
var dialogResult = dialog.ShowDialog();
if (dialogResult == DialogResult.OK)
{
string fileName = null;
if (webResponse.Headers["Content-Disposition"] != null)
{
fileName = webResponse.Headers["Content-Disposition"].Replace("inline; filename=", "").Replace("\"", "");
}
if (fileName.IsEmpty())
{
fileName = "web-api-response";
}
var path = Path.Combine(dialog.SelectedPath, fileName);
using (var stream = File.Create(path))
{
await webResponse.GetResponseStream().CopyToAsync(stream);
}
System.Diagnostics.Process.Start(path);
}
}
else
{
using var reader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8);
response.Content = await reader.ReadToEndAsync();
}
}
/// <see cref="http://stackoverflow.com/questions/219827/multipart-forms-from-c-sharp-client" />
private static async Task<byte[]> GetMultipartFormData(Dictionary<string, object> postParameters, string boundary, StringBuilder requestContent)
{
var needsCLRF = false;
var sb = new StringBuilder();
using var stream = new MemoryStream();
foreach (var param in postParameters)
{
if (needsCLRF)
{
await WriteToStream(stream, requestContent, "\r\n");
}
needsCLRF = true;
if (param.Value is ApiFileParameter file)
{
sb.Clear();
sb.AppendFormat("--{0}\r\n", boundary);
sb.AppendFormat("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", param.Key, file.FileName ?? param.Key);
foreach (var key in file.Parameters.AllKeys)
{
sb.AppendFormat("; {0}=\"{1}\"", key, file.Parameters[key].Replace('"', '\''));
}
sb.AppendFormat("\r\nContent-Type: {0}\r\n\r\n", file.ContentType ?? "application/octet-stream");
await WriteToStream(stream, requestContent, sb.ToString());
await stream.WriteAsync(file.Data.AsMemory(0, file.Data.Length));
requestContent.AppendFormat("<Binary file data here (length {0} bytes)...>", file.Data.Length);
}
else
{
var postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
boundary,
param.Key,
param.Value);
await WriteToStream(stream, requestContent, postData);
}
}
await WriteToStream(stream, requestContent, "\r\n--" + boundary + "--\r\n");
stream.Position = 0;
byte[] formData = new byte[stream.Length];
await stream.ReadAsync(formData);
return formData;
}
private static async Task WriteToStream(MemoryStream stream, StringBuilder requestContent, string data)
{
await stream.WriteAsync(Encoding.UTF8.GetBytes(data).AsMemory(0, Encoding.UTF8.GetByteCount(data)));
requestContent.Append(data);
}
private static void SetTimeout(HttpWebRequest webRequest)
{
#if DEBUG
// Just for debugging.
webRequest.Timeout = 1000 * 60 * 5;
#endif
}
}
}