Retrieve the single value from webapi by using Angular - asp.net

Goal: retrieve the single value "test" in Angular.
Problem: I get an error message
Error: SyntaxError: Unexpected token e in JSON at position 1 at JSON.parse ()
What syntax am I missing?
ASP.NET
// https://localhost:44353/api/Jobadvertisement/VVValidate/4
[HttpGet("VVValidate/{id:int}")]
public ActionResult<String> VVValidate(int id)
{
return "test";
}
Angular
const url = environment.url
let asdfasdf2 = url + 'api/Jobadvertisement/VVValidate/' + "4";
var dsfsdssf = this.http.get(asdfasdf2).subscribe(data => {
console.log(data);
});

Can you please try using httpOptions in Angular with something like this:
const url = environment.url;
let asdfasdf2 = url + 'api/Jobadvertisement/VVValidate/' + "4";
var dsfsdssf = this.http.get(asdfasdf2, { responseType: 'text' }).subscribe(data => { console.log(data); });

You need to change ActionResult to string:
[HttpGet("VVValidate/{id:int}")]
public string VVValidate(int id)
{
return "test";
}

Angular's HttpClient expects JSON data per default, which is why you get a JSON deserialization error upon returning plain text from your API.
You need to pass responseType: 'text' as option to the get method, in order to prevent HttpClient from treating your data as JSON:
httpClient.get("url", { responseType: 'text' });
You can find more information regarding this in Angular's Documentation.
Even though your API code works as it is, I'd like to point out two things:
You can unwrap your String and remove the ActionResult, as it is not needed.
I would encourage you to use the string type instead of System.String. For a detailed explanation, please refer to this.
Your code would look like this after applying these changes:
[HttpGet("VVValidate/{id:int}")]
public string VVValidate(int id)
{
return "test";
}

Related

How can I use a default value/model on WebAPI EmptyBody?

I have dotnet WebAPI and I'm trying to get a specific behaviour but am constantly getting 415 responses.
I have reproduced this by starting a new webapi project using dotnet new webapi on the command line. From there, I added two things: a new controller, and a model class. In my real project the model class is obviously a bit more complex, with inheritance and methods etc...
Here they are:
[HttpGet("/data")]
public async Task<IActionResult> GetModel(BodyParams input)
{
var response = new { Message = "Hello", value = input.valueOne };
return Ok(response);
}
public class BodyParams {
public bool valueOne { get; set; } = true;
}
My goal is that the user can call https://localhost:7222/data with no headers or body needed at all, and will get the response - BodyParams will be used with the default value of true. Currently, from postman, or from the browser, I get a 415 response.
I've worked through several suggestions on stack and git but nothing seems to be working for me. Specifically, I have tried:
Adding [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] into the controller, but this makes no difference unless I provide an empty {} json object in the body. This is not what I want.
Making BodyParams nullable - again, no change.
Adding .AddControllers(opt => opt.AllowEmptyInputInBodyModelBinding = true)... again, no change.
I Implemented the solution suggested here using the attribute modification in the comment by #HappyGoLucky. Again, this did not give the desired outcome, but it did change the response to : 400 - "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true."
I tried modifying the solution in (4) to manually set context.HttpContext.Request.Body to an empty json object... but I can't figure out the syntax for this because it need to be a byte array and at that point I feel like I am way over complicating this.
How can I get the controller to use BodyParams with default values in the case that the user provides no body and no headers at all?
You can achieve that using a Minimal API.
app.MapGet("/data",
async (HttpRequest httpRequest) =>
{
var value = true;
if (Equals(httpRequest.GetTypedHeaders().ContentType, MediaTypeHeaderValue.Parse("application/json")))
{
var bodyParams = await httpRequest.ReadFromJsonAsync<BodyParams>();
if (bodyParams is not null) value = bodyParams.ValueOne;
}
var response = new {Message = "Hello", value};
return Results.Ok(response);
});
So, as there doesn't seem to be a more straightforward answer, I have currently gone with the approach number 5) from the OP, and just tweaking the code from there very slightly.
All this does is act as an action which checks the if the user has passed in any body json. If not, then it adds in an empty anonymous type. The behaviour then is to use the default True value from the BodyParams class.
The full code for the action class is:
internal class AllowMissingContentTypeForEmptyBodyConvention : Attribute, IActionModelConvention
{
public void Apply(ActionModel action)
{
action.Filters.Add(new AllowMissingContentTypeForEmptyBodyFilter());
}
private class AllowMissingContentTypeForEmptyBodyFilter : IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
if (!context.HttpContext.Request.HasJsonContentType()
&& (context.HttpContext.Request.ContentLength == default
|| context.HttpContext.Request.ContentLength == 0))
{
context.HttpContext.Request.ContentType = "application/json";
var str = new { };
//convert string to jsontype
var json = JsonConvert.SerializeObject(str);
//modified stream
var requestData = Encoding.UTF8.GetBytes(json);
context.HttpContext.Request.Body = new MemoryStream(requestData);
}
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
// Do nothing
}
}
}
Then you can add this to any of your controllers using [AllowMissingContentTypeForEmptyBodyConvention]

Unable to upload file as FormData to ASP.net from Angular

I am trying to upload a file from my angular code to an ASP.net backend.
My Angular code sends the object using FormData:
public uploadFiles(files) {
console.log(files);
if(files.length < 1) return;
const formData = new FormData();
files.forEach(file => {
console.log(file);
formData.append(file.name, file);
})
this._http.postFile('/order-processing/import-orders','application/x-www-form-urlencoded' ,formData).pipe(finalize(() => {
console.log("Finalized");
})).subscribe((val: any) => {
console.log('ORDER SUBMITTED', val);
}, error => {
console.log(error);
});
}
With the post file method looking like:
public postFile(path: string, contentType:string, body: FormData) : Observable<any> {
let headers = {
'Content-Type': contentType,
'Authorization': this.authToken
}
return this._http.post(environment.API_URL + path, body, {
headers
});
}
My ASP.net endpoint looks like:
[HttpPost, Route("hospitality/order-processing/import-orders")]
[RequestSizeLimit(2147483648)]
[DisableRequestSizeLimit]
public IActionResult UploadFile()
{
try
{
//var req = Request.Form.Files;
var file = Request.Form.Files;
string folderName = "Uploads";
string webRootPath = _hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
return Json("Upload Successful.");
}
catch (Exception e)
{
return Json("Failed:" + e);
}
}
If I check the network tab on my browser when I send the file, it says that my object is in the call, great, but for some reason it doesn't get picked up on the backend and when I step through the code it is not there.
I get different errors when I modify this code slightly. The error for the code in the state it is in now is "Form key or value length limit 2048 exceeded", however sometimes I get array out of bounds errors, or content boundary limit exceeded errors, it's enough to make you want to slam you face into your keyboard continually.
The whole point of this is to be able to upload an excel file to ASP.net code running in an AWS lambda, which then inserts rows in a RDS database. Am I going about this the right way? Is there a better way to achieve what I am trying to do? If not then what is wrong with my code that doesn't allow me to upload a file to a Web API?!
Thanks
It seems that you're trying to set the limit of the request but the message states that the problem is with form key or value length.
Try setting the RequestFormLimits and check if that helps.
[HttpPost, Route("hospitality/order-processing/import-orders")]
[RequestFormLimits(KeyLengthLimit = 8192, ValueLengthLimit = 8192)]
public IActionResult UploadFile()

Angular 2 HTTP post no HTTP-resource found

I am using Angular 2 in order to send http requests to the server.
The server is running with ASP.Net.
My API:
public class LagerController : ApiController
{
public IHttpActionResult RepHistorie(string vnr, string lagerort)
{
...
}
}
So I would call the API with
http://123.456.7.89/api/lager?vnr=W17291092373&lagerort=0382691395
This works fine when using a tool called postman with wich you can test API's.
But when making a post request with Angular 2, it doesn't work.
It says that the HTTP-resource is not found.
Angular 2:
submit() {
var body = 'vnr=W17291092373&lagerort=0382741400';
var link = 'http://123.456.7.89/api/lager';
this.http.post(link, body)
.subscribe(data => {
console.log(data);
}, error => {
console.log("Oooops!");
});
}
It seems like the parameters aren't added correctly.
Edit: Changed tag
This needs clarification, since the API above seems to be a GET Request.
In case it is a POST Request , then you should use the whole URL while running the API
In case you want to submit an object , you should use [FromBody] as a parameter
Example
[HttpPost]
public IHttpActionResult( [FromBody]YourObject item ) {
}
====
Client-side
var postUrl = 'http://123.456.7.89/api/lager?vnr=W17291092373&lagerort=0382691395';
var postObject = {};
http.post(postUrl,postObject)
For GET request where you would like to use QueryString
[HttpGet]
public IHttpActionResult RepHistorie([FromQuery]string vnr,[FromQuery]string lagerort){
...
}
====
// Query string can be built using RequestOptionsArgs as well
var builtUrl = 'http://123.456.7.89/api/lager?vnr=W17291092373&lagerort=0382691395';
http.get(builtUrl)
Alternative way is to
var getUrl = 'http://webapi/api/lager';
var requestOptions = {};
http.get(getUrl,requestOptions);
Reference:
Angular HTTP: https://angular.io/api/http/Http , check get() and post() methods
RequestOptionsArgs : https://angular.io/api/http/RequestOptionsArgs

Using Meteor with Angular2/Typescript, how do I call an external web service from server-side code based on a client-side event?

When the user adds a new entry in the client, I need to make a web service call from the server (the client-side code will not have access) and add some additional information to the data stored in MongoDB.
Trying to use the standard Meteor.methods/Meteor.call pattern does not seem to work.
Do I need to listen for the click event on the "Add" button on both the server and the client?
Should I raise a custom event on the client that the server reacts to?
Is there a proper way to make a direct call to a server-side method?
Most importantly, how do I keep TypeScript happy in all of this?
I am new to the TypeScript layer on top of Meteor and it is throwing me for loop. I have been generally following the Angular-Meteor tutorial for 2.0 but this sort of thing is not covered yet.
Yes, you can call directly from the server to the web service in order to receive data. I am not so sure how you did for the Meteor.methods/Meteor.call and say it did not work. But basically, the idea is client will click the button and then button will trigger a method on the server. The server method then will call the web service and return the data.
Some example code could be:
Template['template'].events({
'click .getData': function(event: any) {
Meteor.call('serverMethod', function(err, res) {
if (err) {alert(err);}
else { ... }
)
}
});
The tricky part for new comer when calling the rest is you need to use aysnc calling in order to return the data to client. We normally make helper function for that
public static get(url: string, headers: any): any {
var httpCall = Meteor.wrapAsync(HTTP.call);
var result = httpCall('GET', url, {headers: headers});
if (result.statusCode == 200) {
try {
var res = JSON.parse(result.content);
return res;
} catch(err) {
return result.content;
}
}
return null;
}
And call the helper like this
public static serverMethod(username: string, password: string): any {
var response = RestService.get(query.url, query.header);
return response;
}
This way, the res in the client code above will get the result.
I actually dropped the Angular 2 for the lack of documentation, but stay with Typescript for my system because I can wrap all the meteor call inside the Typescript class, as you can see in my example, serverMethod is in the typescript function format, not in meteor way like Meteor.methods({....}), which is really good for now
Typically, this is a class in my server folder
// server/rest.service.ts
declare var RestService: any;
RestService = class RestService {
methodMap = {
"getFromRest": RestService.get,
"postToRest": RestService.post,
};
constructor() {
var abstractService = new AbstractService();
abstractService.registerMethod(this.getClassName(), this.methodMap);
}
getClassName(): string {
return this.constructor.toString().match(/\w+/g)[1];
}
//------------------------------------------------------------------------------------
// Helper methods
//------------------------------------------------------------------------------------
public static get(url: string, headers: any): any {
var httpCall = Meteor.wrapAsync(HTTP.call);
var result = httpCall('GET', url, {headers: headers});
if (result.statusCode == 200) {
try {
var res = JSON.parse(result.content);
return res;
} catch(err) {
return result.content;
}
}
return null;
}
I have a class to map the typescript service to the meteor method
// server/abstract.service.ts
declare var AbstractService: any;
AbstractService = class AbstractService {
constructor() {}
public registerMethod (scopeName: string, methodMap: {[key:string]:any}) {
var scopeMap: {[key:string]: any} = {};
for (var key in methodMap) {
scopeMap[scopeName + '.' + key] = methodMap[key];
}
Meteor.methods(scopeMap);
}
Using angular2, Meteor and Typescript, what works is to chain the Meteor.methods.
First on the client, in response to a button click
...
Meteor.call('importCsv',id,function(error,result) {
...
In collections/methods folder or similar, I define the method as follows:
Meteor.methods({
'importCsv': function(id) {
console.log('importCsv method on client');
Meteor.call('importCsvServer',id);
}
});
In server/ folder, a file includes the method as follows
Meteor.methods({
'importCsvServer': function(id) {
....
In server/main.ts I import the collections/methods/filename. In client/app.ts I import the same thing. The client Meteor.call successfully calls the first method which then calls the second one in the server/ folder.
My goal is to have a bunch of processing on the server initiated by the client. When I had the function calls in the method defined in collections/methods imported into both the client and server, it resulted in compiler errors.
Angular2-Meteor issue 74

Angular JS - send data to .NET Controller API

I have this angular js code here:
$http.post('/reports/', JSON.stringify($scope.user));
and its hitting my Reports Controller Post method:
[HttpPost]
public dynamic Post(Array data){
//do something
}
but when I check the data in my Post method when it hits in my breakpoint it appears as null :( how do I pass the data from $scope.user to my Controller. I did a console.log of $scope.user and the data is there, it is an object but trying to pass it in as JSON.
I found this:
public HttpResponseMessage Post([FromBody]Customer cust)
{
var newCust = _Repository.InsertCustomer(cust);
if (newCust != null)
{
var msg = new HttpResponseMessage(HttpStatusCode.Created);
msg.Headers.Location = new Uri(Request.RequestUri + newCust.ID.ToString());
return msg;
}
throw new HttpResponseException(HttpStatusCode.Conflict);
}
would I have to put [FromBody] Reports report instead of Array data
Just do this simple as possible, you are missing the parameter name:
$http.post('/reports/', {data: $scope.user});
Make sure that $scope.user is an Array, else change the type.

Resources