Form has enctype="multipart/form-data" but Request Content-Type is application/x-www-form-urlencoded; charset=UTF-8 - forms

I have a basic form in ASP.Net MVC4 using Html helpers. I have an input file in the form for uploading a file which will be added to a database. In my view model I have a property for the input file:
public HttpPostedFileBase AssetFile { get; set; }
In my view I have the form helper:
#using (Html.BeginForm("Create", "Contact", FormMethod.Post, new { enctype = "multipart/form-data" }))
Inside my form:
#Html.TextBoxFor(model => model.AssetFile, new { #type = "file", #name = "AssetFile" })
Yet, when I post the form there are no files in the Request.Files. Then I noticed that in Fiddler the Request Header has Content-Type: application/x-www-form-urlencoded; charset=UTF-8. I have another form with an input file and the header has Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryCfBFSR0RXQRnasfb and this form works fine. I tried doing this without Html helpers and same thing happens. The view model itself inherits from another view model where the input file actually belongs but then I have a string property mapped to a textbox and the form is picking up this value. There is no nested forms (on this page this is the only form) but I am having the same problem with another page that has multiple form (not nested) using multiple partial views that contain the forms like this:
#using (Html.BeginForm("Edit", "Home", FormMethod.Post, new { #enctype = "multipart/form-data" })){ #Html.Partial("EditorTemplates/_Profile", Model.InstitutionProfileInformation)}
Thanks in advance for any help.
OK - Here's the weirdness. Since they're (the original coder(s)) using partial views they ajaxified this thing. When the partial view (with the form) is rendered:
var loadContactDiv = function (e) {
isChanged = false;
var url = e.href;
$("#Contacts").load(url, function (response, status, xhr) {
if (status == "error") {
var msg = "Sorry but there was an error: ";
}
$("#Contacts").find('form').submit(divSubmitHandler).change(function () {
isChanged = true;
});
$("#ReturnButton").click(function () {
loadContacts();
});
});
return false;
};
Then when the user click the submit button:
var divSubmitHandler = function () {
var form = $("#Contacts").find('form');
var test = form.serialize();
debugger;
$.ajax({
url: (form).attr('action'),
data: form.serialize(),
type: "POST",
success: function (data) {
if (data == "") {
loadContacts();
} else {
$("#Contacts").html(data);
$("#Contacts").find('form').submit(divSubmitHandler).change(function () {
isChanged = true;
});
$("#ReturnButton").click(function (e) {
loadContacts();
});
}
}
});
return false;
};
Still stuck: http://prntscr.com/20v2cp

Since you are not submitting the form, but using $.ajax to process the request remotely and then get the result, the enctype is ignored from the form itself.
As you can also see the form data is serialize and sent.
So the fix here is simple, to submit the content-type correctly, just add a
content-type
option to the ajax request like so,
var divSubmitHandler = function () {
var form = $("#Contacts").find('form');
var test = form.serialize();
debugger;
$.ajax({
url: (form).attr('action'),
**contentType: 'multipart/form-data',**
data: form.serialize(),
type: "POST",
success: function (data) {
if (data == "") {
loadContacts();
} else {
$("#Contacts").html(data);
$("#Contacts").find('form').submit(divSubmitHandler).change(function () {
isChanged = true;
});
$("#ReturnButton").click(function (e) {
loadContacts();
});
}
}
});
return false;
};
This should do the trick. However if it does not work, please refer to Jquery Form Ajax submit.
jQuery AJAX submit form
Have a nice session!

Related

How to Pass Form Data in Post Request using ionic-native-http in capacitor?

I have used a Validation Form to take the input from users and I have a submit button on which submit() function is being called.
insertuserdata() is a function in service.ts file where api is being called. I am passing form data in the api. File(e.g pdf,doc,docx etc.) can also be passed to the api as multi part form data.
But I am using Ionic-Native-Http to pass multi part form data using capacitor.
I am getting the error as status 415.Please help with this.
submit()
{
this.isSubmitted = true;
if (!this.ionicForm.valid) {
console.log('Please provide all the required values!')
return false;
} else {
console.log(this.ionicForm.value)
}
console.log("this is deptid", this.deptid)
console.log("this is description", this.ionicForm.controls["description"].value)
console.log("this is cid", this.ionicForm.controls["subquery"].value)
console.log("this is location", this.ionicForm.controls["location"].value)
console.log("query",this.ionicForm.controls["query"].value)
console.log("Insert Form Data")
const formData = new FormData();
formData.append('postedby', this.employeeId);
formData.append('description', this.ionicForm.controls["description"].value);
formData.append('location', this.ionicForm.controls["location"].value);
formData.append('deptid', this.deptid);
formData.append('cid', this.ionicForm.controls["subquery"].value);
formData.append('file', this.ionicForm.get('profile').value);
console.log("Insert Api call")
this.c2s.insertuserdata(formData).then((insertdata: any) =>{
console.log("Insert Response",insertdata)
})
insertuserdata(formData) {
let httpOptions = {
headers: new HttpHeaders({
'enctype': 'multipart/form-data;',
'Content-Type': 'application/json'
})
};
this.insert = this.http1.post("https://webapplnapp.tatapower.com/tpc_restfull_service/api/connecttosolve/postQuery", formData, httpOptions);
return this.insert
}

SharePoint List Form Textfield to a dropdown

I've found a video from SharePointTech that explains how to change a textfield to a dropdown list on a List Form using data from open API. I'm trying to recreate it, but I'm hitting a roadblock with the new SharePoint Online. Instead of using "Country/Region", I created a new custom list with Company_Name. I took the person's code and made little changes that made a reference to "WorkCountry". When I save the changes (stop editing), the changes do not reflect and I get the same textfield. I had to use SharePoint Designer 2013 to create a new TestNewForm for new entry. Has anyone been able to reproduce this in SharePoint 2013 Designer? If so, would you be able an example?
I use jQuery's ajax method.
Updated code for your reference(you need to change the list name to your list name,InternalName is also):
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
var demo = window.demo || {};
demo.nodeTypes = {
commentNode: 8
};
demo.fetchCountries = function ($j) {
$.ajax({
url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/lists/getbytitle('Company_Name')/items",
type: "get",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
$j('table.ms-formtable td.ms-formbody').contents().filter(function () {
return (this.nodeType == demo.nodeTypes.commentNode);
}).each(function (idx, node) {
if (node.nodeValue.match(/FieldInternalName="Country_x002f_Region"/)) {
// Find existing text field (<input> tag)
var inputTag = $(this).parent().find('input');
// Create <select> tag out of retrieved countries
var optionMarkup = '<option value="">Choose one...</option>';
$j.each(data.d.results, function (idx, company) {
optionMarkup += '<option>' + company.Title + '</option>';
});
var selectTag = $j('<select>' + optionMarkup + '</select>');
// Initialize value of <select> tag from value of <input>
selectTag.val(inputTag.val());
// Wire up event handlers to keep <select> and <input> tags in sync
inputTag.on('change', function () {
selectTag.val(inputTag.val());
});
selectTag.on('change', function () {
inputTag.val(selectTag.val());
});
// Add <select> tag to form and hide <input> tag
inputTag.hide();
inputTag.after(selectTag);
}
});
},
error: function (data) {
console.log(data)
}
});
}
if (window.jQuery) {
jQuery(document).ready(function () {
(function ($j) {
demo.fetchCountries($j);
})(jQuery);
});
}
</script>
My source list:
Test result:
Updated:
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
var demo = window.demo || {};
demo.nodeTypes = {
commentNode: 8
};
demo.fetchCountries = function ($j) {
$.ajax({
url: 'https://restcountries.eu/rest/v1/all',
type: "get",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
$j('table.ms-formtable td.ms-formbody').contents().filter(function () {
return (this.nodeType == demo.nodeTypes.commentNode);
}).each(function (idx, node) {
if (node.nodeValue.match(/FieldInternalName="Country_x002f_Region"/)) {
// Find existing text field (<input> tag)
var inputTag = $(this).parent().find('input');
// Create <select> tag out of retrieved countries
var optionMarkup = '<option value="">Choose one...</option>';
$j.each(data, function (idx, company) {
optionMarkup += '<option>' + company.name + '</option>';
});
var selectTag = $j('<select>' + optionMarkup + '</select>');
// Initialize value of <select> tag from value of <input>
selectTag.val(inputTag.val());
// Wire up event handlers to keep <select> and <input> tags in sync
inputTag.on('change', function () {
selectTag.val(inputTag.val());
});
selectTag.on('change', function () {
inputTag.val(selectTag.val());
});
// Add <select> tag to form and hide <input> tag
inputTag.hide();
inputTag.after(selectTag);
}
});
},
error: function (data) {
console.log(data)
}
});
}
if (window.jQuery) {
jQuery(document).ready(function () {
(function ($j) {
demo.fetchCountries($j);
})(jQuery);
});
}
</script>
The difference in API will not have a great effect, the key is here '$ j.each (data, function (idx, company) {'. The structure of the return value of different APIs are different, you need to find useful data in return value.

Angular 6 Downloading file from rest api

I have my REST API where I put my pdf file, now I want my angular app to download it on click via my web browser but I got HttpErrorResponse
"Unexpected token % in JSON at position 0"
"SyntaxError: Unexpected token % in JSON at position 0↵ at JSON.parse (
this is my endpoint
#GetMapping("/help/pdf2")
public ResponseEntity<InputStreamResource> getPdf2(){
Resource resource = new ClassPathResource("/pdf-sample.pdf");
long r = 0;
InputStream is=null;
try {
is = resource.getInputStream();
r = resource.contentLength();
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok().contentLength(r)
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new InputStreamResource(is));
}
this is my service
getPdf() {
this.authKey = localStorage.getItem('jwt_token');
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/pdf',
'Authorization' : this.authKey,
responseType : 'blob',
Accept : 'application/pdf',
observe : 'response'
})
};
return this.http
.get("http://localhost:9989/api/download/help/pdf2", httpOptions);
}
and invocation
this.downloadService.getPdf()
.subscribe((resultBlob: Blob) => {
var downloadURL = URL.createObjectURL(resultBlob);
window.open(downloadURL);});
I resolved it as follows:
// header.component.ts
this.downloadService.getPdf().subscribe((data) => {
this.blob = new Blob([data], {type: 'application/pdf'});
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement('a');
link.href = downloadURL;
link.download = "help.pdf";
link.click();
});
//download.service.ts
getPdf() {
const httpOptions = {
responseType: 'blob' as 'json')
};
return this.http.get(`${this.BASE_URL}/help/pdf`, httpOptions);
}
I solved the issue in this way (please note that I have merged multiple solutions found on stack overflow, but I cannot find the references. Feel free to add them in the comments).
In My service I have:
public getPDF(): Observable<Blob> {
//const options = { responseType: 'blob' }; there is no use of this
let uri = '/my/uri';
// this.http refers to HttpClient. Note here that you cannot use the generic get<Blob> as it does not compile: instead you "choose" the appropriate API in this way.
return this.http.get(uri, { responseType: 'blob' });
}
In the component, I have (this is the part merged from multiple answers):
public showPDF(fileName: string): void {
this.myService.getPDF()
.subscribe(x => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([x], { type: "application/pdf" });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob, fileName);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
link.href = data;
link.download = fileName;
// this is necessary as link.click() does not work on the latest firefox
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data);
link.remove();
}, 100);
});
}
The code above works in IE, Edge, Chrome and Firefox. However, I don't really like it, as my component is pulluted with browser specific stuff which will surely change over time.
For Angular 12+, I came up with something like this:
this.ApiService
.getFileFromApi()
.pipe(take(1))
.subscribe((response) => {
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(new Blob([response.body], { type: response.body.type }));
const contentDisposition = response.headers.get('content-disposition');
const fileName = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].trim();
downloadLink.download = fileName;
downloadLink.click();
});
The subscribe is on a simple get() with the Angular HttpClient.
// api-service.ts
getFileFromApi(url: string): Observable<HttpResponse<Blob>> {
return this.httpClient.get<Blob>(this.baseApiUrl + url, { observe: 'response', responseType: 'blob' as 'json'});
}
You can do it with angular directives:
#Directive({
selector: '[downloadInvoice]',
exportAs: 'downloadInvoice',
})
export class DownloadInvoiceDirective implements OnDestroy {
#Input() orderNumber: string;
private destroy$: Subject<void> = new Subject<void>();
_loading = false;
constructor(private ref: ElementRef, private api: Api) {}
#HostListener('click')
onClick(): void {
this._loading = true;
this.api.downloadInvoice(this.orderNumber)
.pipe(
takeUntil(this.destroy$),
map(response => new Blob([response], { type: 'application/pdf' })),
)
.subscribe((pdf: Blob) => {
this.ref.nativeElement.href = window.URL.createObjectURL(pdf);
this.ref.nativeElement.click();
});
}
// your loading custom class
#HostBinding('class.btn-loading') get loading() {
return this._loading;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
In the template:
<a
downloadInvoice
[orderNumber]="order.number"
class="btn-show-invoice"
>
Show invoice
</a>
My answer is based on #Yennefer's, but I wanted to use the file name from the server since I didn't have it in my FE. I used the Content-Disposition header to transmit this, since that is what the browser uses for a direct download.
First, I needed access to the headers from the request (notice the get method options object):
public getFile(): Observable<HttpResponse<Blob>> {
let uri = '/my/uri';
return this.http.get(uri, { responseType: 'blob', observe: 'response' });
}
Next, I needed to extract the file name from the header.
public getFileName(res: HttpResponse<any>): string {
const disposition = res.headers.get('Content-Disposition');
if (!disposition) {
// either the disposition was not sent, or is not accessible
// (see CORS Access-Control-Expose-Headers)
return null;
}
const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; |$)/;
const asciiFilenameRegex = /filename=(["'])(.*?[^\\])\1(?:; |$)/;
let fileName: string = null;
if (utf8FilenameRegex.test(disposition)) {
fileName = decodeURIComponent(utf8FilenameRegex.exec(disposition)[1]);
} else {
const matches = asciiFilenameRegex.exec(disposition);
if (matches != null && matches[2]) {
fileName = matches[2];
}
}
return fileName;
}
This method checks for both ascii and utf-8 encoded file names, prefering utf-8.
Once I have the file name, I can update the download property of the link object (in #Yennifer's answer, that's the lines link.download = 'FileName.ext' and window.navigator.msSaveOrOpenBlob(newBlob, 'FileName.ext');)
A couple of notes on this code:
Content-Disposition is not in the default CORS whitelist, so it may not be accessible from the response object based on the your server's configuration. If this is the case, in the response server, set the header Access-Control-Expose-Headers to include Content-Disposition.
Some browsers will further clean up file names. My version of chrome seems to replace : and " with underscores. I'm sure there are others but that's out of scope.
//Step: 1
//Base Service
this.getPDF() {
return this.http.get(environment.baseUrl + apiUrl, {
responseType: 'blob',
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Authorization': localStorage.getItem('AccessToken') || ''
})
});
}
//Step: 2
//downloadService
getReceipt() {
return new Promise((resolve, reject) => {
try {
// {
const apiName = 'js/getReceipt/type/10/id/2';
this.getPDF(apiName).subscribe((data) => {
if (data !== null && data !== undefined) {
resolve(data);
} else {
reject();
}
}, (error) => {
console.log('ERROR STATUS', error.status);
reject(error);
});
} catch (error) {
reject(error);
}
});
}
//Step 3:
//Component
getReceipt().subscribe((respect: any) => {
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement(‘a’);
link.href = downloadURL;
link.download = “sample.pdf";
link.click();
});
This also works in IE and Chrome, almost the same answer only for other browsers the answer is a bit shorter.
getPdf(url: string): void {
this.invoiceService.getPdf(url).subscribe(response => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
const newBlob = new Blob([(response)], { type: 'application/pdf' });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const downloadURL = URL.createObjectURL(newBlob);
window.open(downloadURL);
});
}

Model binding issue with property name as "Id"

Actually in one request, i am getting the data from the ajax call and then posting the data back in another request back to the controller and here i am using model binding,
here i have the following issues,
1. The dictionary in the bound object is null
2. The Id property [value received at Ajax response] but not received at controller during modelbinding
since i am not storing any values in the hidden fields and am just caching the data and passing to other requests, how do i handle the id and dictionary issues in ASP.Net MVC2.
EDIT
*Model in C#:*
public class ViewObject
{
public string Id {get;set;}
public string Name {get;set;}
}
AJAX Code
function fillExistingViews() {
$.ajax({
url: "..",
data: {
ViewId: $("#View_Id").val()
},
type: "POST",
success: function (data) {
if (data !== undefined) {
var ViewObj = JSON.parse(data);
if (ViewObj.ViewObjects === undefined) {
return false;
}
//ViewObj.ViewObjects = dictionary<string,ViewObject
for (var vo in ViewObj.ViewObjects) {
// HERE I GET ViewObj.ViewObjects[vo].Id
$.ajax({
url: "..",
type: "POST",
contentType: "application/json",
data: JSON.stringify(ViewObj.ViewObjects[vo]),
// on posting the viewobject to the server, i find ViewObject.Id to be null
success: function (data) {
//..
}
});
}
}
}
});
}

Refresh a ListBox in ASP.NET MVC 2 with jQuery

I've looked around on SO to see if this has been asked before and couldn't find anything (so if it has indeed been asked before then I apologize.
Here's what I'm trying to do, a user can select from a list of skills for their profile, if a skill they want isn't in the list then they can add it to the database, I have that accomplished with WCF & jQuery AJAX. Here's the code for that:
$("#AddNewSkill").click(function () {
$("#AddNewSkill").attr("disabled", true);
$("#newSkill").attr("disabled", true);
var newSkill = $("#newSkill").val();
var data = { name: $("#newSkill").val(), description: "", type: "Skill" };
data = JSON.stringify(data)
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "../WeddingPhotographerService.svc/AddNew",
data: data,
dataType: "json",
success: function () {
successCall('#newSkill', '#AddNewSkill');
//$('#SkillListViewContainer').load('../Account/GetSkillControl');
},
error: function (msg) {
$("#AddSkillError").text(msg.d);
$("#AddSkill").attr("disabled", false);
$("#NewSkill").attr("disabled", false);
}
});
});
Here's the method in the AJAX-Enabled WCF service:
[OperationContract]
public bool AddNew(string name, string description, string type)
{
switch (type)
{
case "":
goto default;
case "skill":
IRepository<Skill> skillRepo = ObjectFactory.GetInstance<IRepository<Skill>>();
var skill = new Skill { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
skillRepo.Save(skill);
return true;
case "equipment":
IRepository<Equipment> eqRep = ObjectFactory.GetInstance<IRepository<Equipment>>();
var eq = new Equipment { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
eqRep.Save(eq);
return true;
case "occasion":
IRepository<Occassion> occRepo = ObjectFactory.GetInstance<IRepository<Occassion>>();
var oc = new Occassion { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
occRepo.Save(oc);
return true;
default:
IRepository<Skill> repo = ObjectFactory.GetInstance<IRepository<Skill>>();
var s = new Skill { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
repo.Save(s);
return true;
}
}
It's kind of ugly but I'll optimize it once I have this 2nd part working. Here's how the ListBox is being loaded in the view:
<%: Html.ListBox("Skills", Model.SkillList, new { #style = "width:157px; height:90px;background:#e2f0f1;", #size = "3", #class = "inputbox" })%>
Which comes from RegistrationModelView.cs, here's SkillList in my model view:
private MultiSelectList GetSkills(string[] selectedValues)
{
List<Skill> list = new List<Skill>();
IRepository<Skill> skills = ObjectFactory.GetInstance<IRepository<Skill>>();
foreach (Skill skill in skills.GetAll())
{
list.Add(new Skill()
{
Key = skill.Key,
Name = skill.Name,
Description = ""
});
}
return new MultiSelectList(list, "Key", "Name", selectedValues);
}
And the action in AccountController.cs that loads the view
[MoveFormsScript]
public ActionResult Index()
{
return View(new RegistrationModelView());
}
I'm pretty sure all the code I posted (other than how the new item is added with the WCF service and the jQuery for consuming said service is irrelevant but I thought I'd offer as much information as possible).
Like I said the new value is added to the database no problem, my issue is updating the ListBox to reflect the new values. Anyone got any ideas and can help with this?
Well I mucked around until I found something that does what I need it to do. It may not be the most efficient or elegant way to accomplish the task but it at least works (Maybe someone will come along with a different solution some day).
What I ended up doing was make another $,ajax() call in the success of the first call like this
$("#AddNewSkill").click(function () {
$("#AddNewSkill").attr("disabled", true);
$("#newSkill").attr("disabled", true);
var data = { name: $("#newSkill").val(), description: "", type: "skill" };
data = JSON.stringify(data)
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "../WeddingPhotographerService.svc/AddNew",
data: data,
dataType: "json",
success: function () {
$.ajax({
type:"POST",
datatype:"json",
url: "../Account/GetSkills",
success:updateSkillsListBox
});
},
error: function (msg) {
alert(msg.d);
}
});
});
function updateSkillsListBox(data, status) {
$("#Skills").html("");
for(var d in data) {
$("<option value=\"" + data[d].Value + "\">" + data[d].Name + "</option>").appendTo("#Skills");
}