The client application accesses a web api controller to get a set of data , the controller has a list as parameter.
static void Main(string[] args)
{
Program p = new Program();
p.getdata().Wait();
}
public async Task getdata()
{
List<string> datelist = new List<string>();
datelist.Add("12/05/2017");
datelist.Add("14/05/2017");
datelist.Add("18/05/2017");
HttpClient host = new HttpClient();
host.BaseAddress = new Uri("http://localhost/widgetApi/");
host.DefaultRequestHeaders.Clear();
host.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
StringContent content = new StringContent(JsonConvert.SerializeObject(datelist), Encoding.UTF8, "application/json");
HttpResponseMessage response = await host.GetAsync("api/dataAccessApi?"+ datelist);
response.EnsureSuccessStatusCode();
if( response.IsSuccessStatusCode)
{
Console.Read();
}
}
The controller is
public HttpResponseMessage Get([FromBody] List<string> dates)
{
..... function going here
}
My question is how can I pass datelist to the web api ??
GET requests do not have a BODY, you can use query string values. ?dates='{UrlEncoded date}'&dates='{UrlEncoded date}'...
public HttpResponseMessage Get([FromUri] List<string> dates) {
//..... function going here
}
On the client side you would need to construct the query string and append it to the Uri
var dates = datelist.Select(d => string.Format("dates={0}", UrlEncode(d));
var query = string.Join("&", dates);
var uri = "api/dataAccessApi?" + query;
With UrlEncode looking like this
/// <summary>Escape RFC3986 String</summary>
static string UrlEncode(string stringToEscape) {
return stringToEscape != null ? Uri.EscapeDataString(stringToEscape)
.Replace("!", "%21")
.Replace("'", "%27")
.Replace("(", "%28")
.Replace(")", "%29")
.Replace("*", "%2A") : string.Empty;
}
For Asp.Net Core consider to use [FromQuery] instead of [FromUri]
Related
I am new to RestAPI and am not sure how to call one and pass it credentials. I have a Windows Forms with a "POST" button. This project is built using .Net framework 4.5. When user clicks on this button it executes code with the class below. In this calls there is a Post method that calls a RestAPI. This RestApi requires basic authentication. Any help would be great.
public static class CallRestAPI
{
private static readonly string baseURL = "https://servername/fscmRestApi/resources/11.13.18.05/erpintegrations";
public static async Task<string> Post(string name, string job)
{
var inputData = new Dictionary<string, string>
{
{"name", name },
{"job", job }
};
var input = new FormUrlEncodedContent(inputData);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage res = await client.PostAsync(baseURL + "users", input))
{
using (HttpContent content = res.Content)
{
string data = await content.ReadAsStringAsync();
if (data != null)
{
return data;
}
}
}
}
return string.Empty;
}
}
You have to add the authorization header:
var byteArray = Encoding.ASCII.GetBytes($"{UserName}:{Password}");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
I am trying to create a basic test web api, and use a standard controller to test call it.
When I run it, by putting
http://localhost:55144/home/testapi
it'll run the catcher function and completely ignore the parameter.
Then, the catcher will happily return a value, which can be seen in the calling code.
I have tried various combinations of putting [FromBody], changing the type of the parameter in TestApiMethod, and seeing if making a list or array makes any difference.
I've noticed a couple of weird things:
- I'm not using the parameter in the code of TestApiMethod, but Visual Studio is not giving me an unused variable warning.
- If I make the type of the parameter testString a string or even an int, the code below will route to the catcher. If I make it some variation of a model or a Jobject, it will not. It gets as far as running
HttpResponseMessage response = await client.PostAsJsonAsync("api/activity", sendData);
then just returns to the web page.
Here's the code:
Models
public class testStringModel
{
public string testString { get; set; }
}
public class apiResponse
{
public string response { get; set; }
}
Home controller calling Api:
public void TestApi()
{
Task myTask = testApiCall();
}
private async Task<string> testApiCall()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:55144");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
testStringModel data = new testStringModel { testString = "cheese" };
string jsonData = JsonConvert.SerializeObject(data);
var sendData = new StringContent(jsonData, Encoding.UTF8, "application/json");
//var sendData = new Dictionary<string, string>
//{
// {"testString", "cheese"}
//};
HttpResponseMessage response = await client.PostAsJsonAsync("api/activity", sendData);
string responseBodyAsText = await response.Content.ReadAsStringAsync();
dynamic stuff = JObject.Parse(responseBodyAsText);
string finalResponse = stuff.response;
return finalResponse;
}
}
The api:
namespace ApplicationActivity
{
public class ActivityController : ApiController
{
[System.Web.Http.HttpPost]
public HttpResponseMessage Catcher()
{
apiResponse apiResponseObject = new apiResponse();
apiResponseObject.response = "You have somehow wound up in the catcher";
string json = JsonConvert.SerializeObject(apiResponseObject);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent(json, Encoding.Unicode, "application/json");
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(20)
};
return response;
}
[System.Web.Http.HttpPost]
public HttpResponseMessage TestApiMethod(string testString)
{
apiResponse apiResponseObject = new apiResponse();
apiResponseObject.response = "OK from test";
string json = JsonConvert.SerializeObject(apiResponseObject);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent(json, Encoding.Unicode, "application/json");
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(20)
};
return response;
}
}
}
Please will you tell me what I'm doing wrong with my code, how to fix it and what is happening when the code doesn't get to the catcher?
Thanks.
It turns out that I was using an older version of visual studio and as a result the whole thing got really confused with whether is was running .net core or not.
Upgrading to the latest and making sure the latest .net core is installed solved most of my troubles
I am looking for how to cache a request in ASP.NET Core 2.x?
I have API proxy which always return a different response using the same request (synonyms composition using an AI, hence that's why I am not looking for caching the response).
And I would like to cache the request since it's always the same (always the same basic auth and parameters to poke the other API that I am proxy-ing).
Since the request use a file input.xml for the parameters, I am wondering where I can cache that one as well
My controller:
[Route("api/v1/[controller]")]
public class CompositionController : Controller
{
[HttpGet]
public async Task<string> Get(string transformation = "xml")
{
var httpClient = new HttpClient();
const string authScheme = #"Basic";
const string name = #"myUserName";
const string password = #"myPassword";
var authBytes = Encoding.ASCII.GetBytes($#"{name}:{password}");
var auth64BaseString = Convert.ToBase64String(authBytes);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authScheme, auth64BaseString);
const string fileName = #"input.xml";
var inputBytes = File.ReadAllBytes(fileName);
var byteArrayContent = new ByteArrayContent(inputBytes);
const string formDataKey = #"""file""";
const string formDataValue = #"""input.xml""";
var multipartFormDataContent = new MultipartFormDataContent()
{
{ byteArrayContent, formDataKey, formDataValue }
};
const string url = #"http://baseurl:port/my/resource/is/there.do?transformation=" + transformation;
var response = await httpClient.PostAsync(url, multipartFormDataContent);
return await response.Content.ReadAsStringAsync();
}
}
You really shouldn't be constructing an HttpClient every time the endpoint is called.
This is what I would do:
//create a service that caches HttpClient based on url
public interface IHttpClientService
{
IHttpClient GetClient(string baseHref);
void AddClient(HttpClient client, string baseHref);
}
//implement your interface
public class HttpClientService : IHttpClientService
{
private readonly ConcurrentDictionary<string, IHttpClient> _httpClients;
public HttpClientService()
{
_httpClients = new ConcurrentDictionary<string, IHttpClient>();
}
public void AddClient(HttpClient client, string baseHref)
{
_httpClients.
.AddOrUpdate(baseHref, client, (key, existingHttpClient) => existingHttpClient);
}
public IHttpClient GetClient(string baseHref)
{
if (_httpClients.TryGetValue(baseHref, out var client))
return client;
return null;
}
}
//register as singleton Startup.cs
services.AddSingleton<IHttpClientService, HttpClientService>();
//inject into Controller
[HttpGet]
public async Task<string> Get(string transformation = "xml")
{
const string url = #"http://baseurl:port/my/resource/is/there.do?transformation=" + transformation;
var httpClient = _httpService.GetClient(url);
if(httpClient == null)
{
httpClient = new HttpClient(url);
const string authScheme = #"Basic";
const string name = #"myUserName";
const string password = #"myPassword";
var authBytes = Encoding.ASCII.GetBytes($#"{name}:{password}");
var auth64BaseString = Convert.ToBase64String(authBytes);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authScheme, auth64BaseString);
const string fileName = #"input.xml";
var inputBytes = File.ReadAllBytes(fileName);
var byteArrayContent = new ByteArrayContent(inputBytes);
const string formDataKey = #"""file""";
const string formDataValue = #"""input.xml""";
var multipartFormDataContent = new MultipartFormDataContent()
{
{ byteArrayContent, formDataKey, formDataValue }
};
_httpClient.AddClient(httpClient, url);
}
else
{
//You can cache your MultipartFormDataContent in MemoryCache or same cache as HttpClient
//Get MultipartFormDataContent from cache and
}
var response = await httpClient.PostAsync(url, multipartFormDataContent);
return await response.Content.ReadAsStringAsync();
}
I am doing an MVC 5 Application, and I am calling a API controller method that is in another Solution.
I am using HttpClient(). and I am calling PostAsJsonAsync with some parameters, an instance of a class.
It looks like this.
string apiUrl = "localhost:8080/api/";
ContactWF contactWF = new contactWF();
contactWF.contact_id=0;
contactWF.UserOrigin_id=20006
contactWF.ProcessState_id=2;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync(apiUrl + "Contact/Method", contactWF);
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<int>().Result;
}
}
My API controller method is like this.
[ActionName("Method")]
[HttpGet]
public int Method([FromBody] ContactWF userwf)
{
return 10;
}
It Works fine...
My problem is when I try Serialized the parameter class instance
I replace line
HttpResponseMessage response = await client.PostAsJsonAsync(apiUrl + "Contact/Method", contactWF);
with this one
string jsonData = JsonConvert.SerializeObject(contactWF);
HttpResponseMessage response = client.PostAsJsonAsync("api/Contact/Method", jsonData).Result;
I've got an Error:405...
It looks like the Json string it is not recognize as a Parameter.
My Json string looks like this.
"{\"Contact_id\":0,\"Description\":null,\"ProcessState_id\":2,\"Type_id\":0,\"Object_id\":0,\"Parent_id\":null}"
that is ContactWD class converter to json.
What´s wrong?
Method PostAsJsonAsync serialize parameter object himself, so it serialized your json string again.
If you need serialize object himself for some reason, then use method HttpClient.PostAsync
string jsonData = JsonConvert.SerializeObject(contactWF);
var stringContent = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync("api/Filler/CountMensajeByUser", stringContent);
Change verb to HttpPost in your api controller
[ActionName("Method")]
[HttpPost]
public int Method([FromBody] ContactWF userwf)
{
return 10;
}
Update
You don't need to serialize object in PostAsJsonAsync
HttpResponseMessage response = client.PostAsJsonAsync("api/Contact/Method", contactWF).Result;
Take a look at sample code from microsoft
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing
internal class NewIdeaDto
{
public NewIdeaDto(string name, string description, int sessionId)
{
Name = name;
Description = description;
SessionId = sessionId;
}
public string Name { get; set; }
public string Description { get; set; }
public int SessionId { get; set; }
}
//Arrange
var newIdea = new NewIdeaDto("Name", "", 1);
// Act
var response = await _client.PostAsJsonAsync("/api/ideas/create", newIdea);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
In the process of learning about web API, I have created (or actually Visual Studio has) a simple controller. I have also created a WPF program that can read from web API's GET() part, but I can't access the others.
I have tried a lot and found many blogs and pages that say: "Just do like this..." but nothing works. What am I doing wrong?
MVC part:
namespace MVCWebservice.Controllers
{
public class LaLaController : ApiController
{
// GET: api/LaLa
public string Get()
{
return "Hello from API";
}
// GET: api/LaLa/5
public string Get(int id)
{
return "value";
}
// POST: api/LaLa
public void Post([FromBody]string value)
{
var a = value;
}
// PUT: api/LaLa/5
public void Put(int id, [FromBody]string value)
{
var b = value;
int c = id;
}
// DELETE: api/LaLa/5
public void Delete(int id)
{
int c = id;
}
}
}
And a method from my Console application that actually works:
private static async Task ReadFromWebApi()
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new Uri("http://localhost:26176/");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var resp2 = await client.GetAsync("api/LaLa/");
resp2.EnsureSuccessStatusCode();
var aaa = resp2.Content;
string result = await aaa.ReadAsStringAsync();
Console.WriteLine(result);
}
A method that just stops:
If I remove the EnsureSuccessStatusCode I'll get the following back:
ss = "{\"Message\":\"The requested resource does not support http
method 'PUT'.\"}"
private static async Task SendToWebApi()
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new Uri("http://localhost:26176/");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
Console.WriteLine("-1-");
var resp2 = client.PutAsync("api/LaLa/", new System.Net.Http.StringContent("Hey", Encoding.UTF8, "application/json")).Result;
Console.WriteLine("-2-");
resp2.EnsureSuccessStatusCode();
var ss = await resp2.Content.ReadAsStringAsync();
}
How would I write my access to the other methods?
For one thing, you are calling PUT on the URL api/LaLa/, but from the server's method signature
public void Put(int id, [FromBody]string value)
it appears that the URL should include a numeric id to satisfy the first parameter, e.g. api/LaLa/100 or similar. Perhaps you could modify your client to call PutAsync() like this:
var resp2 = client.PutAsync("api/LaLa/100", new System.Net.Http.StringContent("Hey", Encoding.UTF8, "application/json")).Result;
Does POST work? Look at this answer to a question that looks remarkably like yours, and see if you can get that to work.