How to call Model Binding manually in .Net Core - c#

Anybody know how to easily reuse the built in model binders in .Net Core?
I have to make a call to an api regularly that returns with NamedValuePaired data (like a querystring) Is there an easy way to just take reuse the model binder instead of parsing the data?
I could just take the data and make another call to a controller of my own to do the binding but, there has to be way other than doing a http post to my own controller.
Currently I am doing the following:
var rawResponse = "";
using (var client = new HttpClient())
{
//Post to paypal
var response = await client.PostAsync(_options.TokenUrl, new StringContent(parameters, Encoding.UTF8, "text/namevalue"));
//read response
rawResponse = await response.Content.ReadAsStringAsync();
}
var nameValues = QueryHelpers.ParseQuery(rawResponse);
Model m = new Model();
if (nameValues.ContainsKey("RESULT"))
m.Result = Convert.ToByte(nameValues["RESULT"].FirstOrDefault());
// etc....
Ideally I'd like to take the response and pass it through a the built in model binder like it was being posted to me.

Related

Get Response from 3rd party API .NET CORE

I have a code like:
[HttpPost("ConsumeApiKavling")]
public async Task<HttpStatusCode> ConsumeApiKavling([FromBody] Kavlings model)
{
string apiBaseUrl = configuration.GetValue<string>("WebAPIBaseUrl");
HttpClient client = new HttpClient();
StringContent content = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
var Response = await client.PostAsync(apiBaseUrl, content);
return Response.StatusCode;
}
So when I run, the apiBaseUrl will send a response something like:
{
KavlingID: '4x',
Status: 'OK'
}
From that response, how can I get it? I mean, I want to save it to my table in the database.
Please advise.
Thank you.
Depending on the approach you want to take, you could use the following workflow:
Create the Model that will hold the API response
(Optional): Create a Data Transfer Object (DTO) or ViewModel with the properties that you are going to pass to your data access service/database
Read the body content from the API response with:
string jsonResult = await response.Content.ReadAsStringAsync();
Deserialize the returned JSON object (read JSON into your .NET object)
var responseModel = JsonSerializer.Deserialize<ResponseModel>(jsonResult);
(Optional): Map your Model to your DTO and pass it over to the data access service
NOTE: You could use the JsonSerializerOptions if you want some extra options for your JSON deserialization

"Pass through" controller action (gets and returns JSON) in .NET Core 3.1

Someone's probably done this before but I can't seem to formulate the question properly to find results. I want to make AJAX calls from a view, but I can't directly call the external API from javascript because there's a key that I can't expose. My idea is to have another controller action that I call from the page that calls the actual external REST API I want to get data from and just passes it on as a JSON. I see lots of examples of getting a JSON through C# and deserializing it but not many where you get a JSON and then return it and consume it from the view. Any help appreciated.
public JsonResult GetStuff()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("Stuff/?Id=" + id).Result;
*code to take response and pass it on as a JSON that I can consume from Javascript
}
Here is what I recommend.
[HttpGet("myapi/{id}");
public async Task MyApi(int id) {
// Replace these lines as needed to make your API call properly.
using HttpClient client = new() {
BaseAddress = REMOTE_SERVER_BASE
}
// Make sure to properly encode url parameters if needed
using HttpResponseMessage response = await client.GetAsync($"myapi/{id}");
this.HttpContext.Response.StatusCode = (int)response.StatusCode;
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers) {
this.HttpContext.Response.Headers[header.Key] = new StringValues(header.Value.ToArray());
}
await response.Content.CopyToAsync(this.HttpContext.Response.Body);
}
This will copy all the common response fields such as status code, headers, and body content, over to your response.
This code isn't tested so you might have to tweak it a bit but it should be enough to get you started.

C# reading information from Android

i trying to do a simple login function.
The login will be made by an App and the information goes to a WebService (in C#).
My app is send the information to the server via HttpPost. But i can't get and return this information on the Web Service side
To make the request (android side) i was using:
// Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", user.getText().toString()));
params.add(new BasicNameValuePair("password", pass.getText().toString()));
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
On the WebService side, i was try to use the Serialize method, but it doens't work
Ps.: In order to test, i tried to run on another WebService (this one built with PHP), and works fine.
Any ideas how to make this work??
[Edit]
This is the web service side:
[HttpPost]
public string LogarAplicativo()
{
//Request.InputStream.Seek(0, SeekOrigin.Begin);
string jsonData = new StreamReader(Request.InputStream).ReadToEnd();
dynamic data = JObject.Parse(jsonData);
//DB validation's
var json = "";
var serializer = new JavaScriptSerializer();
json = serializer.Serialize(new { success = "0", message = "Test message!" });
return json;
}
When you send information with UrlEncodedFormEntity, it will look like a the contents of an HTTP form:
param1=value1&param2=value2
This is not JSON data, so your serialization code doesn't work because it is a completely different structure. Parsing form data requires different methods like HttpUtility.ParseQueryString.

Create one model from two sets of raw JSON or return multiple models in my view?

I am making multiple requests to a web API that I've built and it returns raw JSON for each request. Here are the requests:
var client = new WebClient();
string avgRatesOne = client.DownloadString(urlOne);
string avgRatesTwo = client.DownloadString(urlTwo);
var model = JsonConvert.DeserializeObject<MyData.RootObject>(avgRatesOne);
var modelTwo = JsonConvert.DeserializeObject<MyData.RootObject>(avgRatesTwo);
return View();
So how would I be able to make this into one object? Either that or is there a way to access 2 models from my View?

Calling ReadAsFormDataAsync twice

I'm facing a situation where I've to read the form data from incoming request in ASP.NET Web API twice (from model binder and filter). I've tried using LoadIntoBufferAsync but no luck.
// from model binder
Request.Content.LoadIntoBufferAsync().Wait();
var formData = Request.Content.ReadAsFormDataAsync().Result;
// from filter
var formData = Request.Content.ReadAsFormDataAsync().Result;
The problem is that the underlying buffer for content is a forward-only stream that can only be read once.
Why do you need to read it twice? A little more context would help. Is it that you are reading from two separate filters?
EDIT: might try reading directly from MS_HttpContext and using that as your content stream (don't think this works in a self hosted environment).
using (var s = new System.IO.MemoryStream()) {
var ctx = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
ctx.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
ctx.Request.InputStream.CopyTo(s); var body =
System.Text.Encoding.UTF8.GetString(s.ToArray());
}
During the development of a REST API, we had a need to authenticate a request prior to allowing the response to be processed within the controller, and so this created a need to be able to read the header as well as the form (if any) to determine if the credentials were passed into the request within the body of the form rather than through the request header.
A few lines of code reset the stream pointer to the beginning of the stream so that MVC would be able to read the form and populate the view model in the controller
public class WebServiceAuthenticationAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var authenticationHeaderValue = actionContext.Request.Headers.Authorization;
try
{
if (authenticationHeaderValue != null)
{
var webRequestInfo = new WebRequestInfo(actionContext.Request.Method, actionContext.Request.RequestUri);
this.AuthenticationHeaderService.LogOnUsingAuthenticationHeader(authenticationHeaderValue, webRequestInfo);
}
else if (actionContext.Request.Content.IsFormData())
{
Task<NameValueCollection> formVals = actionContext.Request.Content.ReadAsFormDataAsync();
this.AuthenticationFormService.LogOnUsingFormsAuthentication(formVals.Result);
// reset the underlying stream to the beginning so that others may use it in the future...
using (var s = new System.IO.MemoryStream())
{
var ctx = (HttpContextBase) actionContext.Request.Properties["MS_HttpContext"];
ctx.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
}
}
}
catch (Exception)
{
throw;
}
}
}
Initially the data model was not being created by MVC and a null was passed into the controller method. After resetting the stream, MVC was able to read the form, create and populate the data model, and pass it into the controller method.
[WebServiceAuthentication]
public HttpResponseMessage Get(DocumentRequestModel requestForm)
{
var response = CreateResponse(HttpStatusCode.OK);
response.Content = new ByteArrayContent(this.documentService.GetDocument(requestForm.DocumentId.ToString()));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
You really should not need to do that. At the end of the day, HttpContext Stream points to the same stream Web API reads from.
You can try to put LoadIntoBufferAsync in both places as one could trigger before the other and it was already in the buffer, calling LoadIntoBufferAsync has no side effect.
// from model binder
Request.Content.LoadIntoBufferAsync().Wait();
var formData = Request.Content.ReadAsFormDataAsync().Result;
// from filter
Request.Content.LoadIntoBufferAsync().Wait();
var formData = Request.Content.ReadAsFormDataAsync().Result;

Categories