I'm struggling with this issue for several hours, and I can't find any solution.
Does someone used ServiceStack to upload multiple files with one POST request?
I was trying to use PostFile:
FileInfo fi = new FileInfo("ExampleData\\XmlAPI.xml");
var client = new XmlServiceClient("http://localhost:1337");
client.PostFile<DefaultResponse>("/test", fi, "application/xml");
But here I am able to add only one file to the request.
My second shot was to use LocalHttpWebRequestFilter but inside there is only a extension method which also allows to post only one file.
Multiple File Upload APIs have been added to all .NET Service Clients in v4.0.54 that allow you to easily upload multiple streams within a single HTTP request. It supports populating Request DTO with any combination of QueryString and
POST'ed FormData in addition to multiple file upload data streams:
using (var stream1 = uploadFile1.OpenRead())
using (var stream2 = uploadFile2.OpenRead())
{
var client = new JsonServiceClient(baseUrl);
var response = client.PostFilesWithRequest<MultipleFileUploadResponse>(
"/multi-fileuploads?CustomerId=123",
new MultipleFileUpload { CustomerName = "Foo,Bar" },
new[] {
new UploadFile("upload1.png", stream1),
new UploadFile("upload2.png", stream2),
});
}
Or using only a Typed Request DTO. The JsonHttpClient also includes async equivalents for each of the new
PostFilesWithRequest APIs:
using (var stream1 = uploadFile1.OpenRead())
using (var stream2 = uploadFile2.OpenRead())
{
var client = new JsonHttpClient(baseUrl);
var response = await client.PostFilesWithRequestAsync<MultipleFileUploadResponse>(
new MultipleFileUpload { CustomerId = 123, CustomerName = "Foo,Bar" },
new[] {
new UploadFile("upload1.png", stream1),
new UploadFile("upload2.png", stream2),
});
}
Related
I have created API-1 which reads data from a remote database. How to create an API-2 which will read the data from API-1 & display it?
You have given very limited information. Based on that I tried to give you answer. You just add another web API controller API2. As you said your API1 reading information which have httpget. So when you added API2 let see you implement httpGet in API as well and just use HttpClient service class to call API1. Below is code to in API2
using (var client = new HttpClient())
{
client.BaseAddress = new
Uri("http://localhost:XXXXX/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
//GET Method
var responseTask = client.GetAsync("api/API1");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask =
result.Content.ReadAsAsync<string[]>();
}
else
{
Console.WriteLine("Internal server Error");
}
}
This is just example.
As per this documentation you should be able to create an appRoleAssignment via Microsoft Graph, however this doesn't work. In a GitHub issue I was instructed to create the issue here. We have migrated most of our code from Azure Graph API to Microsoft Graph and this is the last piece that is missing.
This finally worked for me!
There might be more optimized ways to post the JSON but I had to go to basics to make sure nothing is causing this to fail behind the scenes.
const string ROLE_ASSIGNMENT_FORMATTER = "https://graph.microsoft.com/beta/servicePrincipals/{0}/appRoleAssignments";
public static async Task AddApplicationUsers(string enterpriseAppId, string userId, string roleId)
{
HttpClient client = new HttpClient();
string url = string.Format(ROLE_ASSIGNMENT_FORMATTER, enterpriseAppId);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetAccessToken());
var roleAssignment = new
{
appRoleId = roleId,
principalId = userId,
resourceId = enterpriseAppId
};
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(roleAssignment), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
return ;
}
else
{
throw new HttpRequestException(response.ReasonPhrase);
}
}
In our ASP.NET MVC web application we send emails as part of scheduled tasks handled by Hangfire for which I am using Postal as described here
The method works fine and we are able to send HTML/text emails. Now we need to generate and attach PDF files as well. The attached PDF needs to be generated dynamically by use of a Razor template. First I tried to use Rotativa in order to generate the PDF. However I encountered the problem that method BuildPdf needs a ControllerContext which is not available in the background HangFire process. I tried to fake the ControllerContext as
using (var memWriter = new StringWriter(sb))
{
var fakeResponse = new HttpResponse(memWriter);
var fakeRequest = new HttpRequest(null, "http://wwww.oururl.com", null);
var fakeHttpContext = new HttpContext(fakeRequest, fakeResponse);
var emailController = new BackgroundEmailController();
var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeHttpContext), new RouteData(), emailController);
var attachment = emailController.BillAttachment(email);
var pdf = attachment.BuildPdf(fakeControllerContext);
if (pdf != null && pdf.Count() > 0)
{
using (MemoryStream ms = new MemoryStream(pdf))
{
var contentType = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Application.Pdf);
email.Attach(new System.Net.Mail.Attachment(ms, contentType));
}
}
}
However this raised a NullReference error in Rotativa.
Then I tried first to compile the template view with RazorEngine to HTML(and then convert the HTML to pdf by some mean) as
var engineService = RazorEngineService.Create();
engineService.AddTemplate(cache_name, File.ReadAllText(billAttachmentTemplatePath));
engineService.Compile(cache_name, modelType: typeof(BillEmail));
var html = engineService.Run(cache_name, null, email);
using (var ms = CommonHelper.GenerateStreamFromString(html))
{
var contentType = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Html);
email.Attach(new System.Net.Mail.Attachment(ms, contentType));
}
And it throws another NullReference in the RazorEngine dynamic DLL:
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_bb2b366aaef64f2bbc2997353f88cc9e.Execute()
at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
I was wondering if anybody have suggestions for generating PDF files from a template in a Hangfire process?
If you are open to commercial solutions, you can try Telerik reporting and export it as pdf programmatically. You define your report and then invoke it to generate PDF on the server side, finally email the byte[] as email attachment. You can now kickoff this process using Hangfire job.
Here is a pseudo code assuming you have defined the structure of your report, Please look here for more details on how to create your report programatically.
public void GenerateAndEmailReport()
{
var reportSource = new InstanceReportSource();
Telerik.Reporting.Report report = new MyReport();
//populate data into report
reportSource.ReportDocument = report;
var reportProcessor = new ReportProcessor();
reportSource.ReportDocument = report;
var info = new Hashtable();
var result= reportProcessor.RenderReport("PDF", reportSource, info);
byte[]reportBytes = result.DocumentBytes;
SendEmail(reportBytes, "myreport.pdf"); // a method that takes the bytes and attach it to email.
}
Additional references from telerik.
send report as email
Generating PDF in console application
Saving a report programmatically
I am trying the following to get list of projects from "on prem" TFS
private static async void Method()
{
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "Username", "Password"))));
using (HttpResponseMessage response = client.GetAsync(
"http://test-test-app1:8080/tfs/boc_projects/_apis/projects?api-version=2").Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
I am using a user name and password which has admin permissions on TFS i am trying to connect.But i get unauthorized access error when i try the above.
The REST API of getting a list of team projects is:
>
http://tfsserver:8080/tfs/CollectionName/_apis/projects?api-version=1.0
Make sure you have enabled Basic Auth for your TFS:
check your IIS to see whether the Basic authentication service role is installed.
go to IIS Manager, select Team Foundation Server -- Authentication
and disable everything other than Basic Authentication. Then do the
same for the tfs node under Team Foundation Server.
restart your IIS.
Here's a simple app using the Catalog Service. It looks for a file by cycling through all Project Collections and Projects, and finds instances of the file by name. It wouldn't take much to change it for your needs.
using System;
using System.Linq;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace EpsiFinder
{
internal class Program
{
// Server URL. Yes, it's hardcoded.
public static string Url = #"http://tfs.someserver.com:8080/tfs";
private static void Main()
{
// Use this pattern search for the file that you want to find
var filePatterns = new[] { "somefile.cs" };
var configurationServerUri = new Uri(Url);
var configurationServer = TfsConfigurationServerFactory.GetConfigurationServer(configurationServerUri);
var configurationServerNode = configurationServer.CatalogNode;
// Query the children of the configuration server node for all of the team project collection nodes
var tpcNodes = configurationServerNode.QueryChildren(
new[] { CatalogResourceTypes.ProjectCollection },
false,
CatalogQueryOptions.None);
// Changed to use the Catalog Service, which doesn't require admin access. Yay.
foreach (var tpcNode in tpcNodes)
{
Console.WriteLine("Collection: " + tpcNode.Resource.DisplayName + " - " + tpcNode.Resource.Description);
// Get the ServiceDefinition for the team project collection from the resource.
var tpcServiceDefinition = tpcNode.Resource.ServiceReferences["Location"];
var configLocationService = configurationServer.GetService<ILocationService>();
var newUrl = new Uri(configLocationService.LocationForCurrentConnection(tpcServiceDefinition));
// Connect to the team project collection
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(newUrl);
// This is where we can do stuff with the team project collection object
// Get the Version Control instance
var versionControl = tfs.GetService<VersionControlServer>();
// Select the branches that match our criteria
var teamBranches = versionControl.QueryRootBranchObjects(RecursionType.Full)
.Where(s => !s.Properties.RootItem.IsDeleted)
.Select(s => s.Properties.RootItem.Item)
.ToList();
// Match the file in the branches, spit out the ones that match
foreach (var item in from teamBranch in teamBranches
from filePattern in filePatterns
from item in
versionControl.GetItems(teamBranch + "/" + filePattern, RecursionType.Full)
.Items
select item)
Console.WriteLine(item.ServerItem);
}
}
}
}
I have some strange problem. I am using Microsoft.AspNet.WebApi.Client.5.2.3 for simple .NET client for API. I want to post some data to API. I am using PostAsJsonAsync method for it.
using (var client = new HttpClient())
{
var adress = new Uri("http://localhost:28906/v1/things?access_token=SOMETOKEN");
var result = await client.PostAsJsonAsync(adress, new ThingModel() { Name = "test"});
}
When I am sending request, my uri is transformed from "http://localhost:28906/v1/things?access_token=SOMETOKEN" to "http://localhost:28906/v1/things/?access_token=SOMETOKEN" ( '/' is inserted before '?'). And request becomes wrong. How can I overcome this? In fact, how can I pass query string and json body?