formData HttpContext.Current.Request.Params always null when using react - c#

I am not sure if the problem is from the front end or the back end. I know the backend at least works when using postman, whenever using react HttpContext.Current.Request.Params is always null. when I print it in my react app before sending the POST request it says there are values.
here is my Js fetch
export async function uploadFile(file, type) {
const formData = new FormData();
formData.append("file", file);
formData.append("fileType", type);
console.log(...formData); //print the correct stuff
try {
const url = `${auth.getApi()}/FileUpload`;
const resp = await fetch(url, {
method: "POST",
body: formData,
headers: new Headers({
Authorization: auth.BasicAuth(),
"Content-Type": "multipart/form-data",
}),
});
return resp;
} catch (e) {
console.log(e);
return e;
}
}
And the is my c# backend code
public class FileUploadController : ApiController
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const string ALLOWED_CORS_ORIGINS = "*";
[BasicAuthenticationFilter(true)]
[HttpPost]
public async Task<IHttpActionResult> Post()
{
try
{
NameValueCollection formData = HttpContext.Current.Request.Params;
string fileType = formData.Get("fileType"); // it says it's null
if (string.IsNullOrEmpty(fileType)) //true

Related

Writing fulfillment webhook for Dialogflow in .NET C#

I am new to working with dialogflow and fairly new to .NET. I have been struggling for a while now to create my fulfillment webhook. I have got it to work with the node.js inline-editor but want to create my own WebhookController in .NET so I can make external API calls/db calls more easily. Here is what I have so far:
I have a really basic whats-app-like UI where a user can input some text which is appended to a chat window and then the javascript is pinged for the chatbot's response:
function userSubmit() {
var userInput = document.getElementById('user-input').value;
$.ajax({
type: "GET",
url: "/Home/CheckIntentAsync",
data: {
userInput: userInput
},
async: true,
contentType: "application/json",
success: function (data) {
var reply = data;
var botNode = document.createElement("div");
botNode.classList.add('chat');
botNode.classList.add('bot-chat');
botNode.innerHTML = reply; // <--- appends chat window with the reply from Dialogflow
chatWindow.appendChild(botNode);
chatWindow.scrollTop = chatWindow.scrollHeight;
console.log(data);
},
error: function () {
var reply = "I didn't quite catch that, can you rephrase? :/";
var botNode = document.createElement("div");
botNode.classList.add('chat');
botNode.classList.add('bot-chat');
botNode.innerHTML = reply;
chatWindow.appendChild(botNode);
}
});
The ajax call pings my HomeController class which connects to Dialogflow:
public class HomeController : Controller
{
private string sessionID = "XXX"; // my session ID
private string projectID = "XXX"; // my projectID
public ActionResult Index()
{
SetEnvironmentVariable();
return View();
}
private void SetEnvironmentVariable()
{
try
{
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", "MY PATH TO SERVICE ACCOUNT PRIVATE KEY IS HERE");
}
catch (ArgumentNullException)
{
throw;
}
catch (ArgumentException)
{
throw;
}
}
[HttpGet]
public async Task<JsonResult> CheckIntentAsync(string userInput)
{
var sessionClient = await SessionsClient.CreateAsync();
var sessionName = new SessionName(projectID, sessionID);
QueryInput queryInput = new QueryInput();
var queryText = new TextInput();
queryText.Text = userInput;
queryText.LanguageCode = "en";
queryInput.Text = queryText;
// Make the request
DetectIntentResponse response = await sessionClient.DetectIntentAsync(sessionName, queryInput);
var reply = response.QueryResult;
return Json(reply, JsonRequestBehavior.AllowGet);
}
}
So far all of the above works a charm with the inline-editor in Dialogflow. I now am creating my webhook fulfilment in .NET and cannot get it to work. My API class looks like this:
public class WebhookController : ApiController
{
private static readonly JsonParser jsonParser =
new JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true));
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
WebhookRequest request;
using (var stream = await Request.Content.ReadAsStreamAsync())
{
using (var reader = new StreamReader(stream))
{
request = jsonParser.Parse<WebhookRequest>(reader);
}
}
// Simply sets the fulfillment text to equal the name of the intent detected by Dialogflow
WebhookResponse webhookResponse = new WebhookResponse
{
FulfillmentText = request.QueryResult.Intent.DisplayName
};
HttpResponseMessage httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(webhookResponse.ToString())
{
Headers = { ContentType = new MediaTypeHeaderValue("text/json") }
}
};
return httpResponse;
}
}
When I run this, I get in the dialogflow console's diagnostic info a 'DEADLINE_EXCEEDED' message however the webhook is doing so little I don't understand why this is?
"webhookStatus": {
"code": 4,
"message": "Webhook call failed. Error: DEADLINE_EXCEEDED."
}
I don't know if I'm supposed to perform some sort of authentication in the webhook as well as in my HomeController?
Some help would be greatly greatly appreciated!!!
Many thanks!
I was getting this same error when i enabled the webhook call from a follow up intent that wasn't mapped (to handler) in fulfillment inline editor.

Upload a file using Angular 6 to Web Api C#

I have Angular CLI application and Dot net core 2.0 Web API. which I need to upload the file from Angular to Web API. from Web API to Server. When I use Http its working fine. When using HttpClient, it's not working.
Here is my component.ts code:
fileChange(event, grdvalue) {
debugger;
let fileList: FileList = event.target.files;
if (fileList.length > 0) {
let file: File = fileList[0];
const formData: FormData = new FormData();
let GridName = grdvalue;
formData.append('file', file, file.name);
formData.append('Id', this.userId);
formData.append('GridValue', GridName)
this.myService.PostFileToAzure(formData).subscribe(details => {
debugger;
},
(error) => {
debugger;
})
}
}
here is My Service Code:
PostFileToAzure(form) {
debugger;
var body = JSON.stringify(form);
return this.http.post(this.baseApiURL + '/Interpretation/InterpreterFileUpload', form);
}
Here is my Web API code:
[HttpPost("InterpreterFileUpload")]
public async Task<IActionResult> InterpreterFileUpload()
{
try
{
var file = Request.Form.Files[0];
HttpRequest formData = HttpContext.Request;
}
}
How to do send the file using HttpClient From Angular to Web API. Am getting an error in web API like 'Incorrect Content-Type: application/json'
At angular site use this code
file: any;
onSelectFile($event, file) {
this.file = file;
}
uploadImage() {
if (this.file.length === 0) {
return;
}
const formData = new FormData();
for (let file of this.file)
formData.append(file.name, file);
const uploadReq = new HttpRequest('POST', `api/FileUpload`, formData, {
reportProgress: true,
});
this.http.request(uploadReq).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
this.progress = Math.round(100 * event.loaded / event.total);
}
});
}
here onSelectFile should be called on input like this
<input #file type='file' multiple (change)="onSelectFile($event, file.files)">
And at your asp.net site use this code
[Produces("application/json")]
[Route("api/[controller]")]
public class FileUploadController : Controller
{
private IHostingEnvironment _hostingEnvironment;
public FileUploadController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
[HttpPost, DisableRequestSizeLimit]
public ObjectResult UploadFile()
{
try
{
var file = Request.Form.Files[0];
string folderName = "Upload";
string webRootPath = _hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
string fileName = "";
if (file.Length > 0)
{
fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(newPath, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
}
return Ok(fileName);
}
catch (System.Exception ex)
{
return BadRequest(ex.Message);
}
}
}
Thanks
https://github.com/BhadraAnirban/angular_file_upload_net_core
The process is to send the base64 string from Angular using POST call in the FormBody and at .net core convert the string to byte.
Note - FormData will not work in AWS Lambda properly, it will corrupt the file. So this is a better approach to send mail or upload file in AWS as well.
HTML -
<form class="row">
<input id="{{id}}" type="file" (change)="FileSelected($event.target.files);">
</form>
<div id="hidden_upload_item" style="display: none;"></div>
Typescript -
FileSelected(files){
this.fileData = <File>files[0];
this.FileName = this.fileData.name;
let reader = new FileReader();
var selectedFile = <File>files[0];
reader.onload = function (readerEvt: any) {
var arrayBuffer = readerEvt.target.result.toString().split('base64,')[1];
document.querySelector('#hidden_upload_item').innerHTML = arrayBuffer;
this.Proceed();
}.bind(this);
reader.readAsDataURL(selectedFile);
}
Proceed()
{
var item = document.querySelector('#hidden_upload_item').innerHTML;
let formdata = new UploadFile();
formdata.data = item;
formdata.name = this.FileName;
this.UploadDocument(formdata)
.subscribe(event => {
// Perform other operations
}, err => {
this.InProgress = false;
});
}
UploadDocument(formData){
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
return this.http.post('/api/document/uploaddocument', formData, httpOptions);
}
.net Core -
[HttpPost("uploaddocument"), DisableRequestSizeLimit]
public IActionResult UploadDocument([FromBody] FileModel fileModel)
{
try
{
var value = fileModel.Data;
var byteArray = Convert.FromBase64String(value);
var fileExtension = Path.GetExtension(fileModel.Name);
var fileName = fileModel.Name;
/// Now save in Database or AWS
}
return Ok();
}
catch (Exception e)
{
throw e;
}
}
public class FileModel
{
public string Data { get; set; }
public string Name { get; set; }
}
I had used the solution of #Elon Musk but Request.Form.Files[0]gives me the Evaluation timeout exception, so to handle it, I have added the Iformfile parameter in Actionmethod in .net side
public IActionResult UploadFile(IFormFile formfile)

Angular 6 Post Request to .NET Core API

I am working with angular 6 trying to send a post request using httpclient , but always receive null body on the server side.
save( rules:RuleModel[]){
let _headers: HttpHeaders = new HttpHeaders({
'Content-Type': 'application/json; charset=utf-8'
});
return this._httpClient.post(AppConfig.BaseUrl,JSON.stringify(rules), {headers:_headers} ); }
and API function
[HttpPost]
public List<Rule> AddTemplateTextRules( [FromBody]Rule[] Rules)
{
try
{
return RuleManager.AddRule(Rules);
}
catch (Exception e)
{
return null;
}
return null;
}
To make a post request in Angular 6 with standard practice you need to do followings:
In the service class:
import {throwError, Observable } from 'rxjs';
import {catchError} from 'rxjs/operators';
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpParams, HttpErrorResponse } from '#angular/common/http';
import { Rule } from 'path';
#Injectable()
export class RuleService {
constructor(private httpClient: HttpClient) { }
private baseUrl = window.location.origin + '/api/Rule/';
createTemplateTextRules(rules: Rules[]): Observable<boolean> {
const body = JSON.stringify(rules);
const headerOptions = new HttpHeaders({ 'Content-Type': 'application/json' });
return this.httpClient.post<boolean>(this.baseUrl + 'AddTemplateTextRules', body, {
headers: headerOptions
}).pipe(catchError(this.handleError.bind(this));
}
handleError(errorResponse: HttpErrorResponse) {
if (errorResponse.error instanceof ErrorEvent) {
console.error('Client Side Error :', errorResponse.error.message);
} else {
console.error('Server Side Error :', errorResponse);
}
// return an observable with a meaningful error message to the end user
return throwError('There is a problem with the service.We are notified &
working on it.Please try again later.');
}
}
In the Component:
export class RuleComponent implements OnInit {
constructor(private ruleService: RuleService) { }
createTemplateTextRules(): void {
this.ruleService.createTemplateTextRules(rules).subscribe((creationStatus) => {
// Do necessary staff with creation status
}, (error) => {
// Handle the error here
});
}
}
Then in the ASP.NET Core API Controller:
[Produces("application/json")]
[Route("api/Rule/[action]")]
public class RuleController : Controller
{
[HttpPost]
public Task<IActionResult> AddTemplateTextRules( [FromBody]Rule[] Rules)
{
try
{
return RuleManager.AddRule(Rules);
}
catch (Exception e)
{
return false;
}
return Json(true);
}
}
Hope it will help you.
With the latest RxJS(Angular 14) here is the way:
Service
Login(phone:string,password:string)
{
let _headers: HttpHeaders = new HttpHeaders({
'accept': 'application/json'
});
return this.http.post(this.url,{username,password},{headers:_headers})
.pipe(map(response=>response));
}
Component
async Login(phone:string,password:string)
{
let token$ = this.authService.Login(phone,password);
let token = await lastValueFrom(token$);
}
Since I was returning just text and not Json from the API, this was my code to handle text response type in the Service. If you're getting a response parse error, explicitly defining the responseType will help since Json is default.
Login(phone:string,password:string)
{
let _headers: HttpHeaders = new HttpHeaders({
'accept': 'text/plain'
});
return this.http.post(this.url+'security/login?phone='+phone+'&password='+password,null,{headers:_headers,responseType:'text'})
.pipe(map(response=>response));
}

angularjs data not posting from javascript to C# and also service reference not connecting

I have two problems. 1 is that the c# function does not get the js values. Even though when I step through the javascript, the values are in fact there.
The other problem is that my c# post returns a 404 not found error and I see that the debugger never even goes to my other service on localhost123.
Any advice?
This is my angularjs code below
var AddToGroupIds = [];
var RemoveFromGroupIds = [];
angular.forEach($scope.vm.mailingLists, function(f){
if(f.Selected == true){
AddToGroupIds.push(f.Id);
}
else{
RemoveFromGroupIds.push(f.Id);
}
});
return $http({
method: 'POST',
url: '/Contacts/UpdateSubscription',
data: { AddToGroups: AddToGroupIds, RemoveFromGroups: RemoveFromGroupIds, email: 'test#test.com' }
})
.then(function (data) { return data.data; })
this is my c# code below:
[HttpPost]
public void UpdateSubscription(List<int> AddToGroups, List<int> RemoveFromGroups, string email)
{
HttpWebRequest req = null;
var text = "";
try
{
req = HttpWebRequest.CreateHttp(string.Format("http://localhost:123/api/Api/ImmediateUpload?AddToGroupIds={0}&RemoveFromGroupIds={1}&email={2}", AddToGroups, RemoveFromGroups, email));
req.PreAuthenticate = true;
req.Credentials = new NetworkCredential("123", "123", "123");
var res = req.GetResponse();
using (var sr = new StreamReader(res.GetResponseStream()))
{
text = sr.ReadToEnd();
}
res.Close();
}
catch (Exception e)
{
text = e.ToString();
}
}
You cannot post multiple parameters that way. See this article. You can use JObject as the article mentions, or create a UpdateSubscriptionRequest model containing the parameters:
public class UpdateSubscriptionRequest
{
public List<int> AddToGroups { get; set; }
public List<int> RemoveFromGroups { get; set;}
public string Email { get; set;}
}
And change your controller action to:
public void UpdateSubscription([FromBody]UpdateSubscriptionRequest request)

Add Property Value in Message Handler

I want to add property value in message handler of asp.net webapi.
scenario is I implement webapi in which authenticationToken is part of different requests but client ask me to accept token in request header. but some applications are already using this api is sending token in request body.
so i don't want to change implementation. I want to read request header and if it contains header then assign that value to property of request.
is it possible?
Yes, it's possible to modify your request uri & formdata in message handler. Here is an example to do that, you can try this. I've added a class APIKeyHandler to handle this, and added handler in my WebApiConfig. I've uploaded a solution in my Git you can check it from Example WebAPI with Modify Request Body in MessageHandler
Code of WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
System.Web.Routing.RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//add the handler class in WebApiConfig
config.MessageHandlers.Add(new APIKeyHandler());
}
}
Code of APIKeyHandler:
public class APIKeyHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var formData = await request.Content.ReadAsFormDataAsync();
if (request.Method.Method.Equals("POST"))
{
request.Content = new FormUrlEncodedContent(ModifyYourFormData(formData, request));
}
request.RequestUri = new Uri(ModifyYourURI(request.RequestUri.ToString(), request), UriKind.Absolute);
return await base.SendAsync(request, cancellationToken);
}
public IEnumerable<KeyValuePair<string, string>> ModifyYourFormData(NameValueCollection source, HttpRequestMessage request)
{
//Add your logic here
string Authorized = "";
try
{
Authorized = request.Headers.GetValues("AuthorizedKey").FirstOrDefault();
}
catch (Exception ex)
{
}
List<KeyValuePair<string, string>> formData;
formData = source.AllKeys.SelectMany(
source.GetValues,
(k, v) => new KeyValuePair<string, string>(k, v)).ToList();
if (!string.IsNullOrEmpty(Authorized))
{
formData.Insert(0, new KeyValuePair<string, string>("AuthorizedKey", Authorized));
}
return formData;
}
public string ModifyYourURI(string uri, HttpRequestMessage request)
{
//Add your logic here
string Authorized = "";
try
{
Authorized = request.Headers.GetValues("AuthorizedKey").FirstOrDefault();
}
catch (Exception ex)
{
}
if (!string.IsNullOrEmpty(Authorized))
{
return uri + "?AuthorizedKey="+ Authorized;
}
else
{
return uri;
}
}
}
Code of ExampleAPIController:
public class ExampleAPIController : ApiController
{
public async Task<string> Post()
{
string returnValue = "OK";
var formKeyValueData = await Request.Content.ReadAsFormDataAsync();
string AuthorizedKey = formKeyValueData["AuthorizedKey"];
return returnValue;
}
public string Get(string AuthorizedKey)
{
string returnValue = "OK";
return returnValue;
}
}
POST Request Calling Example:
GET Request Calling Example:
one more solution I figure out to add dynamic property in request
if (request.Headers.Contains(HeaderName) && request.Method.Method.Equals("POST"))
{
var formData = await request.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(formData);
data.authenticationToken = request.Headers.GetValues(HeaderName).FirstOrDefault();
request.Content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
}

Categories