I have a problem with HttpParams and HttpHeaders after migrating my project from Angular 7 to Angular 8. When I call the API the params are not added. If anyone can help me fix this problem it will be great.
Here is the method in which I define the headers as well as the params.
fetchJson(url: string, parameters ? : any) {
this.token = this.cookieService.get('access_token');
this.contrat_token = this.cookieService.get('contrat_token');
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
headers = headers.append('Authorization', 'Bearer ' + this.token);
headers = headers.append('contrat_token', this.contrat_token);
let params = new HttpParams()
params.set('search', parameters);
console.log('les headers');
console.log(headers);
console.log('params');
console.log(params.toString())
return this._http.get(url, {
headers,
params
}).pipe(map((resp: any) => {
if (resp.status === 401 || resp.status == 401 || resp.status.toString() == "401") {
this.clearCookie();
} else {
let reponse = resp;
if (reponse == -1 || reponse == "-1") {
this.router.navigate(["/"]);
}
}
return resp;
}
And I call this method in my services as follows.
getDetailThematiquePrevNext(id: string, typeBase: string) {
let URL = this.urlDecorator.urlAPIDecorate("DI", "GetDetailThematiqueHeaderPrevNext");
let params = this.urlDecorator.generateParameters({
id: id,
typeBase: typeBase,
});
return this.apiFetcher.fetchJson(URL, params);
}
Reason provided by Cue is correct, You need to use chaining or do what you did for headers
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
headers = headers.append('Authorization', 'Bearer ' + this.token);
headers = headers.append('contrat_token', this.contrat_token);
let params = new HttpParams()
params = params = params.set('search', parameters);
More readable way to write this would be as follows
const headers = new HttpHeaders()
.append('Content-Type', 'application/json')
.append('Authorization', 'Bearer ' + this.token)
.append('contrat_token', this.contrat_token);
const params = new HttpParams().set('search', parameters);
Also, you can drop Content-Type header, as it is json by default
Probably due to lazy parsing. You have to do a get or getAll to access values to determine the state.
HttpParams class represents serialized parameters, per the MIME type application/x-www-form-urlencoded. The class is immutable and all mutation operations return a new instance.
HttpHeaders class represents the header configuration options for an HTTP request. Instances should be assumed immutable with lazy parsing.
You may want to pass your options directly into the instance for both headers and params:
let headers = new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.token,
'contrat_token': this.contrat_token
});
let params = new HttpParams({
search: parameters
});
As #Siraj stated in an answer, there are alternative ways to set values for headers and params such as set...
let headers = new HttpHeaders().set('name', 'value');
let params = new HttpParams().set('name', 'value');
Or append...
let headers = new HttpHeaders().append('name', 'value');
let params = new HttpParams().append('name', 'value');
The important thing to note here is that these methods require chaining otherwise each method creates a new instance.
You could also convert objects like so:
let headerOptions = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.token,
'contrat_token': this.contrat_token
}
let headers = new HttpHeaders();
Object.keys(headerOptions).forEach((key) => {
headers = headers.set(key, headerOptions[key]);
});
It's also worth avoiding any binding of objects by reference, and instead pass as parameters:
return this._http.get(url, {
headers: headers,
params: params
});
And finally, because your type annotation is "any" for the parameters argument, params expects HttpParamsOptions which is a key/value object where values must be a string annotation.
let params = new HttpParams({
search: JSON.stringify(parameters)
});
Try console.log(params.getAll('search')) but, to make sure headers and params are sent, a better place to check will be Network tab in DevTools.
Related
I have a base request like this:
export const request = (options) => {
const headers = new Headers({
'Content-Type': 'application/json',
});
if (Common.getToken()) {
headers.append('Authorization', 'Bearer ' + Common.getToken())
}
const defaults = {headers: headers};
options = Object.assign({}, defaults, options);
return fetch(options.url, options)
.then(response =>
response.json().then(json => {
if (!response.ok) {
return Promise.reject(json);
}
return json;
})
);
};
and my ajax request:
onCreateNewPost(postDataRequest, photoBody) {
const formData = new FormData();
formData.append('photo', photoBody);
formData.append('postData', JSON.stringify(postDataRequest));
return request({
url: API_BASE_URL + '/posts/new-post',
method: 'POST',
body: formData
});
};
where postDataRequest - json object included post title, description etc...
photoBody - image file.
In the backend I have a controller's method:
#PostMapping(value = "/api/posts/new-post")
#PreAuthorize("hasRole('ADMIN')")
public ResponseEntity createNewPost(#CurrentUser UserPrincipal currentUser,
#RequestBody NewPostDataRequest postRequest,
#RequestParam MultipartFile photo) {
// method body
return ResponseEntity.ok(new ActionCompleteResponse(true));
}
but when I send a request, I get Status Code: 400. What is the problem? I can separately send either json data or multipart data, but I can’t figure out how to transfer them together with one request. I tried to put headers without a Content-Type in the request, as in the code below, so that the request itself indicates it, but in response I get code 415.
onCreateNewPost(postDataRequest, photoBody) {
const formData = new FormData();
formData.append('photo', photoBody);
formData.append('postData', JSON.stringify(postDataRequest));
const headers = new Headers({});
if (Common.getToken()) {
headers.append('Authorization', 'Bearer ' + Common.getToken());
}
return request({
url: API_BASE_URL + '/posts/new-post',
headers: headers,
method: 'POST',
body: formData
});
};
What should I do?
Okay, I found the solution:
1. Clear headers data (except Authorization token)
2. Add to #PostMapping consumes = MediaType.MULTIPART_FORM_DATA_VALUE and add #RequestPart to method parameter
ajax request like:
onCreateNewPost(postDataRequest, photoBody) {
const formData = new FormData();
formData.append('post', new Blob([JSON.stringify(postDataRequest)], {
type: "application/json"
}));
formData.append('photo', photoBody);
const headers = new Headers({});
if (Common.getToken()) {
headers.append('Authorization', 'Bearer ' + Common.getToken())
}
return request({
url: API_BASE_URL + '/posts/new-post',
method: 'POST',
headers: headers,
body: formData
});
};
and spring controller like
#PostMapping(value = "/new-post", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#PreAuthorize("hasRole('USER')")
public ResponseEntity createNewPost(#CurrentUser UserPrincipal currentUser,
#RequestPart("post") #Valid PostEntity post,
#RequestPart("photo") #Valid MultipartFile photo) throws IOException {
post.setAuthor(currentUser.getUsername());
post.setAuthorId(currentUser.getId());
post.setCommentsCount(0L);
post.setDate(LocalDate.now());
post.setPhoto(photo.getBytes());
postService.save(post);
return ResponseEntity.ok(new ActionCompleteResponse(true));
}
#Sergey Scream solution is correct, I just want to add some information to clarify the problem.
So if you want to send json and a file using FormData you have to wrap your json in a blob including the type like he did:
formData.append('post', new Blob([JSON.stringify(postDataRequest)], {
type: "application/json"
}));
Adding your json like this will not work:
formData.append('post', JSON.stringify(postDataRequest));
You're setting Content-Type to application/json in request but body to form data in onCreateNewPost. If you removed line 3 from request then your current solution should work.
Also you're setting the Authorization header twice in onCreateNewPost and request.
I have a method that fires a fetch function. It also sets headers over which type I would like to have control.
// Request headers
let headers: HttpHeaders = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
// Checks if user have x-auth-token cookie
const token = getCookie(AUTH.COOKIE_NAME);
if (token !== null) {
headers = {
...headers,
[AUTH.API_TOKEN]: token
};
}
// Request body
const request = {
method,
headers,
body: JSON.stringify(body)
};
if (body || method.toUpperCase() === HttpMethod.GET) {
delete request.body;
}
I have also enum, which holds all allowed headers:
export enum HttpHeader {
ACCEPT = 'Accept',
CONTENT_TYPE = 'Content-Type',
X_AUTH_TOKEN = 'x-auth-token'
}
I already know that you can't specify enum as the expected index value in the ts interface. Can you give me another option to control http request headers? I saw the idea of using an array of objects, but I have the impression that it is a bit of a form over content.
You can use an enum to define a type that has all the enum values as keys using a mapped type (what you can't do is have an enum as an index parameter).
The predefined Record and Partial mapped types will do the job nicely:
type HttpHeaders = Partial<Record<HttpHeader, string>>
export enum HttpHeader {
ACCEPT = 'Accept',
CONTENT_TYPE = 'Content-Type',
X_AUTH_TOKEN = 'x-auth-token'
}
// Request headers
let headers: HttpHeaders = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
headers = {
...headers,
[HttpHeader.X_AUTH_TOKEN]: "token"
};
Play
I am trying to send a custom HTTP Header from the front end app for it to interact with the gateway. This is my angular function:
import {Http, Headers, Response, RequestOptions } from ‘#angular/http’;
getXById(id :number){
let options = nee RequestOptions({ headers : new Headers({“X-fastgate-resource” :”resource_name}) });
return this.http.get( http://url + “/resource”, options)
I expected to see a Header with, “X-fastgate-resource” as a key, and “resource_name” as value.
What I got was this:
Request Headers:
OPTIONS http://url HTTP/1.1
host...
Access-Control-Request-Headers: x-fastgate-resource
You could try out something like below.
let headers = new HttpHeaders();
headers.append('X-fastgate-resource', 'Example');
let options = { headers: headers };
let apiUrl: string = 'http://url';
this.http.get(apiUrl, options);
Hope this helps
Try This code:
import {HttpHeaders} from '#angular/common/http';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
**With params**
const url = 'yourapi';
return this.http.post(url, {
key: value,
key1: value1
},httpOptions);
**Without Params**
const url = 'yourapi';
return this.http.post(url,httpOptions);
Try using the Angular Context.
Angular Context
This is the simplest way I know of passing data, usually to an interceptor
Define a Context Token - usually in the interceptor
export const MY_FILENAME = new HttpContextToken<string>(() => "");
Pass the data
const context = new HttpContext().set(MY_FILENAME, `${name}.pdf`)
return this.httpClient.post(url, pdf, {context: context})
Collect the data. Usually in the interceptor
const fileName = request.context.get(MY_FILENAME)
I'm just playing around the with Fetch API and I came across something that I can't seem to find the answer to. If I create a new request object like so:
let request = new Request('http://www.foo.com', {
method: 'GET',
mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
});
If I then try and modify the URL (append a query string to it) I receive an error in the browser (Cannot assign to read only property). I know that usually with an object, you can set writable to 'True' but does this work the same for a request object?
The reason that I ask is that I am trying to append a querystring to the end of the URL. These options are in another object, which I've got the values of and concatenated into a string so that it's something like:
number=1&id=130&foo=bar etc.
Am I trying to over engineer what I am doing here?
You can copy all the properties from the old request into a new one with a new url like so:
const init = {
method: 'GET',
mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
};
const oldRequest = new Request("https://old-url.com", init);
const newRequest = new Request("https://new-url.com", oldRequest);
console.log(newRequest.mode === oldRequest.mode); // true
console.log(newRequest.headers.get('Content-Type') === oldRequest.headers.get('Content-Type')); // true
console.log(newRequest.method === oldRequest.method); // true
Request object is immutable by design, don't try to change it.
I wish clone() instance method accepted options to be able to to tweak the cloned object, but it doesn't. So I came up with this function:
function appendQuery(urlOrReq, queryString) {
// make sure we deal with a Request object even if we got a URL string
const req = urlOrReq instanceof Request ? urlOrReq : new Request(urlOrReq);
const {
cache, credentials, headers, integrity, method,
mode, redirect, referrer, referrerPolicy, url, body
} = req;
// Don't add query string if there's none
const urlWithQuery = url + (!queryString ? '' : '?' + queryString);
return new Request(urlWithQuery, {
cache, credentials, headers, integrity, method,
mode, redirect, referrer, referrerPolicy, body
})
}
Here's the same in TypeScript (only the first line is different):
function appendQuery(urlOrReq: RequestInfo, queryString?: string): Request {
// make sure we deal with a Request object even if we got a URL string
const req = urlOrReq instanceof Request ? urlOrReq : new Request(urlOrReq);
const {
cache, credentials, headers, integrity, method,
mode, redirect, referrer, referrerPolicy, url, body
} = req;
// Don't add query string if there's none
const urlWithQuery = url + (!queryString ? '' : '?' + queryString);
return new Request(urlWithQuery, {
cache, credentials, headers, integrity, method,
mode, redirect, referrer, referrerPolicy, body
})
}
Here's how it works:
const req = new Request('https://google.com')
appendQuery(req, 'foo=42')
// => Request {method: "GET", url: "https://google.com?foo=42", headers: Headers, destination: "", referrer: "about:client", …}
I start work with angular 2 and typeScript , all work great, but when i work with rest api (POST) in console log i get Response {_body: "", status: 204, statusText: "Ok", headers: Headers, type: 2… } , despite i passed the true param for login this is my code
authentification(){
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
// headers.append('Content-Type', 'application/json');
return this.http.post(this._postUrl,JSON.stringify({username:"abc",password:"abc"}),{headers:headers});
}
and this is my web service
#POST
#Path("/admin")
#Consumes("application/x-www-form-urlencoded")
public User getDate(#HeaderParam("username")String username,#HeaderParam("password")String password) throws Exception{
try {
String passwordC = AESCrypt.encrypt(password);
User u = userService.login(username, passwordC);
return u;
} catch (Exception e) {
return null;
}
}
i think problem in my web service param in string and in angular param is json
any one can help me please and thanks.
For form-urlencoded content type you need to send data in query string format with key=value pairs. You can build this string with the help of URLSearchParams class from http module. In your case it can look something like this:
authentification() {
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
var params = new URLSearchParams();
params.append('username', 'abc');
params.append('password', 'abc'); // .toString() => username=abc&password=abc
return this.http.post(this._postUrl, params.toString(), {headers});
}