HttpClient.post is sucessful but not sending data to json - json

I am trying to store data from my angular app into a JSON. The JSON is stored within the assets folder of my app and I have the app on a server.
I am using HttpClient to POST data to the JSON. It says it is successful but it does not actually send the data.
This is my .ts:
import { HttpClient } from '#angular/common/http'
import { HttpHeaders } from '#angular/common/http'
export class OpscreenComponent implements OnInit {
constructor(private httpService: HttpClient ) { }
ngOnInit() {
var jsonpost = {
"testing": [
{
"anothertest":"here",
"anumber": 1
}
]
}
var headers = new HttpHeaders({
'Content-Type': 'application/json'
})
var options = { headers: headers }
this.httpService.post("http://servername/angularApp/assets/testing.json", jsonpost, options)
.subscribe(
data=> {
console.log("POST Request is Successful ", data )
},
error => {
console.log("Error ", error)
}
)
}
}
I get no error messages and the request is successful because it is logging POST Request is Successful null in the console.
The json is blank to start and after the POST success it is still blank (hence the null in the console.)
Why is it not posting the data to the JSON?
I am using httpClient for get so I know it is imported correctly.

this.httpService.request('POST', `${YourTestUri}`, { body: jsonpost, headers: headers }).pipe( switchMap( (response: any) => ... ) );

What you are trying to do is write files to disk, unfortunately, The HttpClient of Angular can't write files to disk. At least not with the classic implementation.
You probably prefer to add a server who can write to the disk, and your HttpClient will hit the server with the corresponding data.

Related

NestJS - accepting multiple MIME types in request body

I am trying to create an endpoint in a HTTP API that receives data periodically from remote devices.
There is a technological shift happening in this project where devices have previously reported data in XML whereas future implementations will shift towards JSON.
I am writing this API in NestJS (7.x) and TypeScript. Data will be coming in through the same endpoint (POST /) and data format is differentiated by the Content-Type header.
#Controller()
export class IngressController {
constructor(private readonly ingressService: IngressService) {
}
/* ... */
#Post('')
#Header('Cache-Control', 'none')
#HttpCode(HttpStatus.NO_CONTENT)
public async receive(
#Headers('Content-Type') contentType: string,
#Req() req: any,
#Body() body: string,
): Promise<InsertResponse> {
if (IngressController.isJson(contentType)) {
return { inserted: await this.ingressService.insertJsonString(req.body) };
}
if (IngressController.isXml(contentType)) {
return { inserted: await this.ingressService.insertXmlString(req.body) };
}
throw new BadRequestException(contentType, 'Unsupported Content-Type');
}
/* ... */
}
Future devices will report data in JSON (indicated by the Content-Type: application/json header in the HTTP request), legacy devices report in XML (Content-Type: application/xml).
It works splendidly for JSON. However, my problem is that req.body (or body respectively) is an empty object in the XML case. I presume the NestJS middleware is doing something and getting confused by XML, but I have found no hints as to allow XML payloads side-by-side with JSON. I don't mind parsing it manually.
As you suspected NestJS has a built-in bodyparser that will not be able to parse xml. What you could do is to plug in a custom middleware where you decide whether to parse the request body as xml or pass the request on the the next handler.
Something like this should work (I'm using express-xml-bodyparser in this example):
import {NestFactory} from '#nestjs/core';
import {AppModule} from './app.module';
import {Request} from "express";
const xmlParser = require('express-xml-bodyparser');
const xmlParserMidleware = xmlParser();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use((req: Request, res: any, next: any) => {
if (req.path.includes("/api/json-or-xml-handler") && req.header('Content-Type')?.includes('xml')) {
return xmlParserMidleware(req, res, next);
}
next();
});
await app.listen(8020);
}
bootstrap();
Then, in your controller body will either be the parsed json-object or an object representation of your xml:
#Controller()
export class TestControllerController {
#Post('/api/json-or-xml-handler')
receive(#Body() body: any) {
console.log(body);
// ...
}
}

How to intercept call and serve back JSON file dynamically (without "import")

I have a scaffolding of folders and json files to mock an API's paths. When I run npm run start:mock, LocalMockInterceptor gets provisioned and e.g. replaces a call to host/A/B/C by an http call getting locally Folder A/Folder B/C.json. The JSON files get produced by a separate script which is out of scope here. I cannot make use of "import" as many tutorials show because I need a generic solution as the API i am mocking will evolve over time (and so will this scaffolding of folders and files).
/**
* The idea is to only build this into the bundle if we specify so (e.g. on TeamCity, on your localhost), where you don't want to rely
* on external resources for development
* No conditionals in the code bundle! No configuration files or database dependency.
*/
import {
HttpInterceptor,
HttpResponse,
HttpHandler,
HttpRequest,
HttpEvent,
HttpClient,
HttpHeaders
} from '#angular/common/http';
import { Injectable, Injector } from '#angular/core';
import { Observable, of } from 'rxjs';
import { ErrorService } from './error.service';
const devAssetsFolder = 'assets';
#Injectable()
export class LocalMockInterceptor implements HttpInterceptor {
constructor(
private errorService: ErrorService,
private injector: Injector,
private http: HttpClient
) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (request.url.endsWith('.json')) return next.handle(request);
console.log(
` >>> Mock Interceptor >>> ${request.url} has been intercepted`
);
const path = `${devAssetsFolder}${request.url}.json`;
var promise = this.getJSON(path).toPromise();
const jsonheaders = new HttpHeaders();
jsonheaders.set('Content-Type', 'application/json');
let json2;
promise
.then(json => {
console.log(json);
json2 = json;
})
.catch(error => console.log(error));
Promise.all([promise]);
console.log(json2);
return of(
new HttpResponse({ status: 200, body: json2, headers: jsonheaders })
);
}
private getJSON(jsonPath: string): Observable<any> {
return this.http.get(jsonPath);
}
}
The first conditional is to avoid infinite loops since I am sending HTTP requests in my interceptor
Getting the path to the JSON file based on the URL is quite natural
It seemed to me that I have to convert the JSON Observable into a promise so that I can wait for it to complete before rewrapping that json into the returned Observable. When debugging however, it seems Promise.all is not waiting for the promise to complete (json2 is undefined on the next line), and I end up sending an empty http body back...
How to fix this rxjs promise ?
Is inner HTTP calls my only option ?
Is there a way not to rely on promises ? Can you think of a better way to achieve this ?
Did you try just modifying the target URL in your interceptor ? You want to make an API call that return some JSON but instead of calling a dynamic API, you just want to call you static server so it can return predefined JSON.
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const fakeUrl = `${devAssetsFolder}${request.url}.json`;
const fakeRequest = request.clone({url: fakeUrl});
return next.handle(request);
}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (request.url.endsWith('.json')) return next.handle(request);
console.log(
` >>> Mock Interceptor >>> ${request.url} has been intercepted`
);
const path = `${devAssetsFolder}${request.url}.json`;
return this.getJSON(path).pipe(map(result => {
const jsonheaders = new HttpHeaders({ 'Content-Type': 'application/json' });
return
new HttpResponse({ status: 200, body: result, headers: jsonheaders });
}), // you can also add catchError here
);
}
In intercept method you can return an observable. So your getJSON method returns an observable, we added pipe a map function which maps the result to new http response. If your response already has the right headers you don't even need the pipe and map functions you can just do this :
return this.getJSON(path); // it's an observable, so it's OK.

Angular 7 HttpClient post can not parse large request body?

Does angular HttpClient have limit on request body size?
I was doing an angular project that consumes a rest API. The API is simple, it's just replace a JSON file whenever the API endpoint to the file hits using POST method, with new JSON object as the request body.
The code runs well if the new JSON object is small and only have some nodes, However the code returns an error with status code 400 and error message "Error when parsing request" when i'm trying to send big object. Does the error caused by the big object or am i doing it incorrectly?
Last time it returns error is when i tried to send big JSON file with ~1.5MB size, the JSON object seems to be valid as several online json formatters report it as a valid json object.
I'm sorry if i didn't explain my problem well, as i'm not native english user, and i'm also quite new in angular world. Thanks
import { HttpClient, HttpHeaders, HttpErrorResponse } from "#angular/common/http";
import { take, catchError } from "rxjs/operators";
import { throwError, Observable } from "rxjs";
import { Injectable } from "#angular/core";
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': '*'
});
#Injectable()
export class configService {
private _url = 'http://some-api-endpoint/';
constructor(private http:HttpClient) { }
private _save(data):Promise<any> {
return this.http.post(this._url, data, { headers, responseType: 'text', observe:'response' })
.pipe(
take(1),
catchError(err => this._handleError(err))
)
.toPromise();
}
public async updateConfigFile(data): Promise<any> {
try {
const json = JSON.stringify(data, null, 2);
const update = await this._save(json);
if(update.status === 201) {
return {
success: true,
data: update
}
} else {
return {
success: false,
error: update
}
}
} catch(err) {
return {
success: false,
error: err.message
}
}
}
private _handleError(error: HttpErrorResponse): Observable<never>{
let errObj = {};
if(error.error instanceof ErrorEvent) {
const err = error.error
console.warn('An client-side error occured:', {err});
errObj = {
status: 'internal error',
message: error.error.message
}
} else {
console.warn(`Backend returned code ${error.status} `, {error});
errObj = {
status: error.status,
message: error.error
}
}
return throwError(errObj);
}
}

HTTP Native Plugin (IONIC 3)

I'm trying to make a post request using the HTTP cordova plugin. However, for some reason, the JSON data consumed by the Server side is not being formatted correctly (json brakets). Could anyone help me please?
The import:
import { HTTP } from '#ionic-native/http';
The request implementation:
public sendData(sufix, json) {
return new Promise((resolve, reject) => {
this.http.post(URL+sufix, JSON.stringify(json), {'Content-Type': 'application/json'}).then(result => {
resolve(result.data);
}).catch(error => {
reject(error);
});
});
}
The json sended:
{name: 'Test'}
The content received by the server:
=%7B%22name%22%3A%22Test%22%7D
The server implementation:
#Path("/register")
public class RegisterEndPoint {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response registerUser(UserDTO userDTO) {
// Create DAO for persistence
FactoryDAO factory = new FactoryDAO();
UserDAO userDAO = factory.getUserDAO();
// Create user to be persisted
if (!userDAO.userExist(userDTO.getEmail())) {
User user = new User();
user.setPassword(userDTO.getPassword());
user.setEmail(userDTO.getEmail());
user.setName(userDTO.getName());
userDAO.persist(user);
userDAO.commit();
return Response.status(200).build();
}
return Response.status(405).entity(new ErrorDTO("User already registered!")).build();
}
}
The problem seems to be in Native Plugin, so I've changed to the angular http solution, and it works fine. Follow below the solution which I've perform. Thanks everyone who helped me.
The imports required:
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs/Rx'
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/timeout';
AuthProvider:
public sendRequest(sufix, json) {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(URL+sufix, json, options)
.timeout(TIMEOUT_REQUEST*1000)
.do(this.logResponse)
.map(this.extractData)
.catch(this.handleError)
}
private logResponse(res: Response) {
console.log(res);
}
private extractData(res: Response) {
return res.json();
}
private handleError(res: Response | any) {
return Observable.throw(res.json().error || 'Fail to connect to the server');
}
Calling the AuthProvider:
this.authProvider.sendRequest('register', this.signup).subscribe((data) => {
console.log('Success!');
}, (error) => {
console.log(error);
});
Providers included in app.module.ts
import { HttpModule, JsonpModule } from '#angular/http';
can you please try sending the body without making it a string. you can send the JSON Object without stringify. Give it a try :)
**UPDATE
After sending this
{name: 'Test'}
If you are getting name = "test"
Why dont you try like this
var data = JSON.stringify(data);
var obj = {data:data};
//send the obj Object
So it will show as data = "{name:test}"
Now Parse it from the server. Try and let me know :)
if you are trying to make post request using HTTP then try sending request in this format.
let body = new FormData();
body.append('name', 'Test');
this.http.post(<url>,body);
Try and lemme know if it works for you.
Just add this.http.setDataSerializer(‘json’) before do the post

What does "Result" means in a .map operator of an Observable obtained from an http.get request in nativescript/angular

I'm doing the nativescript/angular tutorial and I found something in the code that I don't understand and want some clarification.
In chapter 4 (Nativescript modules) when they do a http.get resquest to retrieve the Grocery List and they get the Observable I notice that it is passed throught some maps operator, here is the code:
import { Injectable } from "#angular/core";
import { Http, Headers } from "#angular/http";
import { Observable } from "rxjs/Rx";
import "rxjs/add/operator/map";
import { Config } from "../config";
import { Grocery } from "./grocery";
#Injectable()
export class GroceryListService {
constructor(private http: Http) {}
load() {
let headers = new Headers();
headers.append("Authorization", "Bearer " + Config.token);
return this.http.get(Config.apiUrl + "Groceries", {
headers: headers
})
.map(res => res.json())
.map(data => {
let groceryList = [];
data.Result.forEach((grocery) => { //<------HERE
groceryList.push(new Grocery(grocery.Id, grocery.Name));
});
return groceryList;
})
.catch(this.handleErrors);
}
handleErrors(error: Response) {
console.log(JSON.stringify(error.json()));
return Observable.throw(error);
}
}
My question is, what does "Result" means in the second .map
Why they don't simply put
data.forEach((grocery) => {
I ask because I'm not sure if it is an object property of the resulting observable from .map(res => res.json) or something else.
Could you point me to some documentation of where does that "Result" come from and what it means?
First of all, this line .map(res => res.json()) parses the response body into a json object. Then the second map allows access to this json object under data argument. The json object represented by data is actually a wrapper around the actual response result data using the Result as the key mapped to the data returned from the backend which follows this security advise HERE. So data.Result is just a key mapped to the actual data returned from the backend. The backend could have used a different name for the key, e.g. secret, then you would do data.secret to obtain the data returned from the server