Why POST using a 'Form' vs using a 'Body'? - asp.net

The title expresses my post succinctly. Though the following side-note expresses my specific situation which sparked the question.
*Side Note: I'm making a web app with an Angular Front-end. I've found that once I encapsulate my data in a 'FormData' object from an
html form template, I am then unable to get those values back from
within client-side Angular (as posts like this one state:
How do i get the content from a formData?
). Though when I POST to ASP.Net with a '[FromForm]' attribute the
values get mapped fine.
So to make things easier I might rather acquire data from the form in
the first place and then use it in a 'body' POST instead of a 'form'
POST. Doing this so that I am passing around straight data I can work
with, instead of data wrapped in a form which I'm unable to work with
client-side.
So, thus, I'm wondering the differences between Posting 'forms' and
posting 'bodies', and whether there's any reason I should use one over
the other?
Update
In ASP.Net Core you can put attributes on your parameter types for REST endpoints. [FromForm] and [FromBody] are what I'm curious about. On the angular side, calling these REST endpoints requires passing either a 'FormData' (for the [FromForm]), or some sort of user defined type (for [FromBody]).
See below for examples.
Update
Here's the signature for the ASP.NET Endpoint for the '[FromBody]':
[HttpPost]
public async Task<IActionResult> CreateToken([FromBody] LoginViewModel model)
{...}
Here's the Angular snippet that calls this '[FromBody]' endpoint, along with the type that is passed in for the POST call:
public creds = {
username: "",
password: ""
}
login(creds): Observable<boolean> {
return this.http.post("api/account/createtoken", creds)
.pipe(
map((data: any) => {
this.token = data.token;
this.tokenExpiration = data.expiration;
this.username = data.username;
return true;
}));
}
Here is the signature for the ASP.NET Endpoint for the '[FromForm]':
[HttpPost]
public async Task<IActionResult> Signup([FromForm] SignupViewModel viewModel)
{...}
and here is the Angular snippet that calls this '[FromForm]' endpoint:
registerUser(formData: FormData) {
const options = {
headers: new HttpHeaders().set('Accept', 'application/json')
};
return this.http.post('api/account/signup', formData, options)
.pipe(
map(() => {
return true;
}),
catchError(this.handleError)
);
}

The FromForm attribute is for incoming data from a submitted form sent by the content type application/x-www-url-formencoded while the FromBody will parse the model the default way, which in most cases are sent by the content type application/json, from the request body

Related

GraphQL.Net List of int as argument

I am trying to build an application using .Net and GraphQL. I need to get materials. not all of them but with the given Ids. When I pass it via playground or client side, I don't have any problem when I debug but I am not sure how to parse in the server side.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<IntGraphType>("ids");
// Do some action to bring datas
// Send data back
}
What am I missing here is there any methods to parse this in to list of int back?
Instead of using a GraphType for retrieving the argument, use the .NET type you want.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<List<int>>("ids");
// Do some action to bring datas
// Send data back
}
you can use MediatR. Create a Query class and pass it to mediateR. In CQRS, Command is used to write on DB(Create/Delete/Update of CRUD) and Query is used to read from DB(Read of CRUD).
create 'GetMaterialsByIdsQuery' and inside it write your code to get your materials by Id. then use it inside 'resolve' like this:
resolve: async context =>
{
var ids = context.GetArgument<List<int>>("ids");
return await mediator.Send(new GetMaterialsByIdsQuery(ids));
})
another way is that you can return somthing like MaterialsRepository.GetMaterialsByIds(ids) instead of using mediatR. But it is not recommended to use repository here. you can create a service class, inside it use your repository and then use your service here.

Always get 400 error when using API POST to an ASP.net controller from a React

I am using .NetCore 5 Entity Framework to search in my data.
When I post the data, I get a 400 error from the api
myController:
public IActionResult Search( [FromBody]string data)
my react:
handleSubmit(event){
event.preventDefault();
const data = JSON.stringify({
searchTitle: this.state.searchTitle,
boolTitle: this.state.boolTitle,
boolSubject:this.state.boolSubject,
searchElement:this.state.searchElement,
refrence:this.state.refrence,
maker:this.state.maker,
subject:this.state.subject,
startTimeArea:this.state.startTimeArea,
endTimeArea:this.state.endTimeArea,
type:this.state.type,
isAvtive:this.state.isAvtive,
enacmentTime:this.state.enacmentTime
});
const headers = {
'Content-Type': 'application/json' ,
'Accept':'application/json'
}
axios.post('/api/searchPost/search',data,{
headers: headers
}).
then(result => console.log(result)).
catch((error) => {
console.log(error);
});
}
I try change [fromBody] to [fromForm] 400 error Fixed but receive value in controller string is null
Please note that this answer was first accepted, then it was not accepted. When looking into this again, it turns out that OP has changed the question without any notice, hence this answer doesn't line up with the question anymore.
The problem here is that this payload
{ searchTitle:this.state.searchTitle }
doesn't match the API controller method signature. You'll have to do it like this:
axios.post('/api/searchPost/search', JSON.stringify(this.state.searchTitle), {headers: {"Content-Type": "application/json"}})
And you'll need to have the controller method signature like this:
public IActionResult Search([FromBody]string searchTitle)
Tested and verified with an asp.net Core 5 API and Postman.

HTTP GET and POST, how to implement in Angular 2 to be able to list, add and remove objects

Okay, so I am new to working with HTTP and actually getting some data from the server. Been sifting through a lot of tutorials, examples and questions asked here, but I am not finding what I want. All tutorials I've found only shows how to retrieve and add some data.
So based on those examples I've managed to retrieve data:
service:
getCases(){
return this.http.get('someUrl');
}
Case component constructor:
this._service.getCases()
.map((res: Response) => res.json())
.subscribe(cases => this.cases = cases);
Adding cases
service:
public saveCase(case: case) {
let body = JSON.stringify(case);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post('someUrl', body, options)
.map(this.extractData)
.catch(this.handleError)
.subscribe(case => this.cases.push(case));
}
Case Component:
saveCase() {
let case = new Case(this.id, this.name, this.date)
this._service.saveCase(case);
this.name = '';
}
Okay, so I have and Array "Cases" which contains Case objects. Getting the data from the server displays the cases like I want them to. When I add a new case it gets sent to the server, but how do I get the Array updated when I add a new Case. Because now the new case appears only after I refresh the browser.
Second question is that the user can click a case and it then routes to a detail list where the user can add steps and feedback. If it matters, case has the attributes id, name, date and an array of steps, at this point the array is empty. The step object is it's own class and the object contains an array of feedback. Feedback is also an own class and the object has two attributes, which are both strings. So it's all nested. When I click the case, it does route to the detail page, but there the case name should be printed and it doesn't. It also shows my button for adding steps, but it does nothing. Obviously I'm missing something in my methods, but I have no clue to as what to do. As a comment I can say that before adding the http in my code it all worked as it should. Here are the methods, that are probably missing something:
Case Component:
gotoDetail(case: Case) {
this._router.navigate(['CaseDetail', {"id": case.name}]);
}
Service:
public getById(id: string): Case {
for (let case of this.cases) {
if (case.id === id) {
return case;
}
}
return null;
}
Then there is the matter of syntax for removing cases, haven't found an example that works for me yet, I've tried a bunch... among others the example links provided by #shershen below. None works. The original methods I have, that should be changed to work with http:
Component:
removeSearchCase(case: Case) {
this._service.removeCase(case);
}
Service:
public removeCase(value: Case): void {
let index = this.cases.indexOf(value);
this.cases.splice(index, 1);
}
So the case removal is with post.
And about the backend I can say as much that I only have the following three posts and gets:
getCases (GET), saveCase (also works as updating the case)(POST) and removeCase (POST).
It's hard to debug without sample demo, however the descriptions quite detailed. I am adding some points that may fix the problem while improving the code structure:
First, you should move the request subscription/observing into the service methods; that will encapsulate the request handling logic in service layer:
//service.ts
#Injectable()
export class service {
getCases(){
if (!this.request) {
this.request = this.http.get('/assets/data.json')
.map((response: Response) => response.json())
.map((data: string[]) => {
this.request = null;
return this.names = data;
});
}
return this.request;
}
}
Second, you need to create an instance of your service in your Component's constructor instead of using it as a static method of the service:
//component.ts
import {MyService} from 'PATH_TO_YOUR_SERVICE';
class CaseComponent {
constructor(private _service : MyService){
//other stuff..
}
getData(){
this._service.getCases()
}
}
Additional references:
Official "Getting and Saving Data with HTTP"
Service example with Observables (with Firebase, but still)
Simple service in Angular2 seed project
I think you should put your cases Array in the CaseComponent:
case.component.ts:
cases: Case[];
constructor(private caseService: CaseService){}
getCases() {
this.caseService.getCases()
.subscribe(cases => this.cases = cases);
}
saveCase() {
let case = new Case(this.id, this.name, this.date);
this.caseService.saveCase(case)
.subscribe(case => this.cases = [...this.cases, case]);
}
case.service.ts:
getCases() {
return this.http.get(this.casesUrl)
.map(this.extractData)
.catch(this.handleError);
}
saveCase (case: Case) {
let body = JSON.stringify({ case });
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(this.casesUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
Then try to change "name" to "id" in gotoDetail:
gotoDetail(case: Case) {
this._router.navigate(['CaseDetail', {"id": case.id}]);
}

Emberjs not accepting array from spring mvc properly

I am new to Ember and not very strong in JavaScript.
I am trying to use Java Spring MVC on a back-end with Ember on the front end. When I am trying to access a page. Ember makes a request for data to the server. Server answers with an Array, but the Array is not retrieved on the Ember side properly. I get an exception.
Error while loading route: Error: No model was found for '0'
at new Error (native)
at Error.Ember.Error (http://localhost:8080/libs/js/ember.js:844:19)
at Ember.Object.extend.modelFor (http://localhost:8080/libs/js/ember-data.js:9805:33)
at JSONSerializer.extend.extractArray (http://localhost:8080/libs/js/ember-data.js:3172:28)
at superWrapper (http://localhost:8080/libs/js/ember.js:1239:16)
at Ember.Object.extend.extractFindAll (http://localhost:8080/libs/js/ember-data.js:2380:21)
at Ember.Object.extend.extract (http://localhost:8080/libs/js/ember-data.js:2365:37)
at http://localhost:8080/libs/js/ember-data.js:10396:34
at invokeCallback (http://localhost:8080/libs/js/ember.js:9753:19)
at publish (http://localhost:8080/libs/js/ember.js:9423:9)
When debuging the javascript i found out that there is for in loop looping over an array
for (var prop in payload)
This for in loop is not retrieving the array elements, but is rather going throw properties. It might be because of what they talk about in this question on SO.
The - for me - problematic code is on github here.
Am I doing something wrong? should I report a bug?
I have created the following Ember model:
App.User = DS.Model.extend({
email: DS.attr('string'),
active: DS.attr('boolean'),
});
This is how the mapping looks like on server side
#RequestMapping("/users")
public
#ResponseBody
List<User> users() {
System.out.println("getting users");
List<User> list = new LinkedList<User>();
list.add(new User(1,"test#b.c"));
list.add(new User(2,"test2#b.c"));
list.add(new User(3,"test3#b.c"));
return list;
}
This JSON I got from fiddler:
[{"id":1,"email":"test#b.c","active":true},{"id":2,"email":"test2#b.c","active":false},{"id":3,"email":"test3#b.c","active":false}]
By default, I believe ember-data expects your JSON response to be prefixed with the model name, even if it's an array. This means you're data will need to be formatted like this:
{
"users":[
{
"id":1,
"email":"test#b.c",
"active":true
},
{
"id":2,
"email":"test2#b.c",
"active":false
},
{
"id":3,
"email":"test3#b.c",
"active":false
}
]
}

Call controller which returns view in javascript

I am working in ASP.Net MVC. I have following Javascript code in which i am calling a controller-method which returns a view. I want to send parameters to a controller method which re
function fun(p1,p2)
{
// code here to call controller method which returns view
}
public ActionResult ProblemDetails(p1,p2)
{
// here goes some code.
return View();
}
Please tell me the code which can be used to call controller and send parameters too.
Action Method
public ActionResult SendStream(string a, string b)
{
}
JQuery/JSON
Please note that Get Verb will not support complex Data parameters due to it's Query string length constraint. So use POST Verb instead of GET Verb while sending large data
$.ajax({
url: url,
data: JSON.stringify({ a: "a", b: "b" }), //Two String Parameters
type: 'GET', //For Submit, use POST
contentType: 'application/json, charset=utf-8',
dataType: 'json'
}).done(function (data) {
//Success Callback
}).fail(function (data) {
//Failed Callback
}).always(function(data) {
//Request completed Callback
});
Are you perhaps looking to return a Partial View? You can use jQuery ajax to post to a controller method that returns a Partial View (html). You can then render that HTML on the page.
http://mazharkaunain.blogspot.com/2011/04/aspnet-mvc-render-partial-view-using.html
jQuery.get is the shorthand way to achieve this.
function fun(p1,p2)
{
var url = '/controller/ProblemDetails?p1=' + p1 + '&p2=' + p2;
$.get(url, function (data) {
// data will be your response as html
});
}
I might also suggest to have the action return PartialView() instead of View() since you will not return the layout along with the response. It all depends on your intentions for the returned html.
There are several ways to do it.
For example Ajax:
Quick note first: Make sure that in your MVC routing configuration you have a route configured to reflect the following url below:
function fun(p1,p2)
{
var url = '/ControllerName/ProblemDetails?p1=p1&p2=p2' //url to your action method
$.ajax({
url:url,
type:'post' or 'get', //(depending on how you're doing this. If post you can pass data internally instead of query string ),
dataType:'html', //(for example)
success:function(data){
//data here will contain your View info so you can append it do div for example. You can use JQuery .html() function for that
error: function (xhr) {
//catch error
}
}
})
}
Another way, in case if you want to load your View data to DIV is to use JQUery functions such as .load();
function fun(p1,p2)
{
var url = '/ControllerName/ProblemDetails?p1=p1&p2=p2';
$('#YourDivTagForExample').load(url);
}
$.ajax call can also be abbriviated to $.get, $.post or $.getJSON depending on what kind of a call you want to make to your action method. There is a lot more to it too.
Finally make sure to take a look at this answer. Your question was actually already answered in full:
Correct way to handle Ajax calls in ASP.Net MVC 3
Use JSONResult instead of ActionResult and Manipulate return data in javascript.

Resources