Angular 4 http get with multiple params to Spring Request Mapping - spring

I'm trying to send a httpClient.get request with params and consume it in my Spring Controller. I want to send search criteria and return a list of objects according to that criteria.
This is my search.service.ts
public getDentists(name, city, type, rating): Observable<Dentist[]>{
let params = new HttpParams();
params.set('name', name);
params.set('city', city);
params.set('type', type);
params.set('rating', rating);
return this.httpClient.get('dentists/', {params: params});
}
in my controller.java
#RequestMapping(value = "/dentists", method = RequestMethod.GET)
public List<Dentist> search(#RequestParam("name") String name,
#RequestParam("city") String city,
#RequestParam("type") String type,
#RequestParam("rating") String rating) {
return dentistRepository.findDentistByName(name);
}
This is what I get as an error:
I also get this error:
2017-12-03 01:07:10.138 WARN 10108 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'name' is not present
My question is what am I doing wrong, why am I not receiving the params with requestparam?

HttpParams is immutable. All the mutation operators return a new instance, so you have to do
let params = new HttpParams()
.set('name', name)
.set('city', city)
.set('type', type)
.set('rating', rating);
or
let params = new HttpParams();
params = params.set('name', name);
params = params.set('city', city);
params = params.set('type', type);
params = params.set('rating', rating);

change your code to:
let params = new HttpParams()
.set('name', name)
.set('city', city)
.set('type', type)
.set('rating', rating);
HttpParams is immutable.

try to use append() method (instead of add()):
public getDentists(name, city, type, rating): Observable<Dentist[]>{
let params = new HttpParams();
params = params.append('name', name);
params = params.append('city', city);
params = params.append('type', type);
params = params.append('rating', rating);
return this.httpClient.get('dentists/', {params: params});
}
UPDATE
actually append() and set() methods will produce exactly the same result:
?name=NAME_VALUE&city-CITY_VALUE&type=TYPE_VALUE&rating=RATING_VALUE,
append() should be used when we want to append another value to existing key, something like:
?name=NAME1&name=NAME2_VALUE&.....
We also can use HttpParamsOptions object passed to HttpParams constructor:
const params = new HttpParams({
fromObject: {
name,
city,
type,
rating
}
});
return this.httpClient.get('dentists/', {params});

Related

How to do I get the form values in Springboot sent from angular?

I am building a formData
const formulario = new FormData();
formulario.append('preco', this.formularioCasaVenda.get('preco').value);
formulario.append('foto_1', this.formularioCasaVenda.get('foto_1').value);
formulario.append('dormitorios', this.formularioCasaVenda.get('dormitorios').value);
I am sending it in a post request.
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'multipart/form-data'})
};
return this.http.post<Casa>("http://localhost:8080/api/casas-venda", formData1).pipe(
tap((product: Casa) => console.log(`added product w/ id=$`)),
catchError(this.handleError<Casa>('addProduct'))
);
How can I access dormitorios, foto_1, preco from the Spring boot controller, so I can populate a model?
My "desperate" atempt.
#CrossOrigin(origins = "http://localhost:4200")
#PostMapping("casas-venda")
public CasaVenda storeCasaVenda(#RequestParam("formulario") MultipartFile[] casaVenda){
CasaVenda casaVendaDB = new CasaVenda();
casaVendaDB.setDormitorios(1);
casaVendaDB.setPreco( Double.parseDouble(casaVenda[1].toString()));
casaVendaDB.setPreco(900.00);
return casaVendaDB;
// return this.casaVendaRepositorio.save(casaVenda);
}
This is one way of solving it:
#CrossOrigin(origins = "http://localhost:4200")
#PostMapping("/casas-venda")
public CasaVenda storeCasaVenda(#RequestParam("dormitorios") Integer dormitorios,
#RequestParam("preco") BigDecimal preco,
#RequestParam("foto_1") MultipartFile foto) {
CasaVenda casaVendaDB = new CasaVenda();
casaVendaDB.setDormitorios(dormitorios);
casaVendaDB.setPreco(preco.doubleValue());
// foto I am assuming is a file, so you receive it and you have to get the InputStream from it.
return casaVendaDB;
// return this.casaVendaRepositorio.save(casaVenda);
}
One hint:
Don't return in the endpoints #Entity classes, it is not a nice practice, instead you should create a DTO (Data Transfer Object) and only return to the Frontend what they need to know.
https://martinfowler.com/eaaCatalog/dataTransferObject.html
I hope I could help you.

Springboot controller request param for map always null

I'm trying to pass in a bunch of id's to create a filter.
The incoming request looks like (ignoring the pagination stuff, which works fine)
http://localhost:8080/news-items?filter%5B%5D=09c731de-7ed8-385d-849c-f4d6535137ab&filter%5B%5D=dd1ba187-2df9-3985-ad1c-a4cde2dfe669&modelPath=controller.newsItems&page=0&per_page=25
Where the filter param equals an ID, but there is a bunch of them, for example:
filter: [
"09c731de-7ed8-385d-849c-f4d6535137ab",
"dd1ba187-2df9-3985-ad1c-a4cde2dfe669"
],
I can't seem to collect the filters in the controller. At the moment I have
public String getFeeds(#RequestParam(value = "filter", required = false) MultiValueMap<String, String> filter, #RequestParam(value = "page", required = false) int page, #RequestParam(value = "per_page", required = false) int perPage) {
log.info("Filter: {}", filter);
}
However filter is always null. I've tried using a String rather than a map but that is also always null.
How do I go about accepting an unknown number of params in this manner? I get the feeling this is really simple but I'm just missing the obvious...
Turns out it was simple like I thought. When using a Map in the #RequestParam it takes all the incoming params, regardless of what they are.
So from what I can tell the correct solution is to do something like
#GetMapping(produces = APPLICATION_JSON)
public String getFeeds(#RequestParam MultiValueMap<String, String> params) {
params.forEach(//something);
}
I think what you are looking for is just an Array or a List, something like below :
public String getFeeds(#RequestParam(value = "filter", required = false) List<String> filters) { ... }

How to use ES Java API to create a new type of an index

I have succeed create an index use Client , the code like this :
public static boolean addIndex(Client client,String index) throws Exception {
if(client == null){
client = getSettingClient();
}
CreateIndexRequestBuilder requestBuilder = client.admin().indices().prepareCreate(index);
CreateIndexResponse response = requestBuilder.execute().actionGet();
return response.isAcknowledged();
//client.close();
}
public static boolean addIndexType(Client client, String index, String type) throws Exception {
if (client == null) {
client = getSettingClient();
}
TypesExistsAction action = TypesExistsAction.INSTANCE;
TypesExistsRequestBuilder requestBuilder = new TypesExistsRequestBuilder(client, action, index);
requestBuilder.setTypes(type);
TypesExistsResponse response = requestBuilder.get();
return response.isExists();
}
however, the method of addIndexType is not effected, the type is not create .
I don't know how to create type ?
You can create types when you create the index by providing a proper mapping configuration. Alternatively a type gets created when you index a document of a certain type. However the first suggestion is the better one, because then you can control the full mapping of that type instead of relying on dynamic mapping.
You can set types in the following way:
// JSON schema is the JSON mapping which you want to give for the index.
JSONObject builder = new JSONObject().put(type, JSONSchema);
// And then just fire the below command
client.admin().indices().preparePutMapping(indexName)
.setType(type)
.setSource(builder.toString(), XContentType.JSON)
.execute()
.actionGet();

Grails: POST or GET httprequest params only

In controller and gsp there are avaliable two variables:
params - tahat contains map of all params (both GET from Query String and POST from forms)
request - the HttpServletRequest object
How can I get POST only params or GET (query string) only params? I need both types of params but in different maps.
I know, that HttpServletRequest object contains information about Query String, but I'm looking for solution without implementing parser of query string.
I have solved this problem by parsing Query String with org.codehaus.groovy.grails.web.util.WebUtils and then I put these data into HttpServletRequest attribute.
Now I can use request.getAttribute('paramsGet') or request.getAttribute('paramsPost').
The Grails filters was very helpfull, so:
package com.selly.filters
import org.codehaus.groovy.grails.web.util.WebUtils
class ParamsFilters {
List globalParams = [
"controller",
"action",
"format"
]
def filters = {
all(controller:'*', action:'*') {
before = {
Map paramsRequest = params.findAll {
return !globalParams.contains(it.key)
}
Map paramsGet = WebUtils.fromQueryString(request.getQueryString() ?: "")
Map paramsPost = paramsRequest.minus(paramsGet)
request.setAttribute('paramsGet', paramsGet)
request.setAttribute('paramsPost', paramsPost)
//println request.getAttribute('paramsGet')
//println request.getAttribute('paramsPost')
}
after = { Map model ->
}
afterView = { Exception e ->
}
}
}
}
Even with Grails, you're working with Servlet which mix up POST and GET. And I don't remember seeing something (except for reparsing the Query String) which would help you.

How to set the default namespace or how to define the #key values when using google-api and using Atom serialization/parser

I'm having trouble with Atom parsing/serializing - clearly something related to the namespace and the default alias - but I can;t figure out what I'm doing wrong.
I have two methods - one that I'm trying to do a GET and see if an album is defined and what that tries to do a POST to create the album (if it does not exist).
The GET I managed to get working - although there too I'm pretty sure I am doing something wrong because it is different from the PicasaAndroidSample. Specifically, if I define:
public class EDAlbum {
#Key("atom:title")
public String title;
#Key("atom:summary")
public String summary;
#Key("atom:gphoto:access")
public String access;
#Key("atom:category")
public EDCategory category = EDCategory.newKind("album");
}
Then the following code does indeed get all the albums:
PicasaUrl url = PicasaUrl.relativeToRoot("feed/api/user/default");
HttpRequest request = EDApplication.getRequest(url);
HttpResponse res = request.execute();
EDAlbumFeed feed = res.parseAs(EDAlbumFeed.class);
boolean hasEDAlbum = false;
for (EDAlbum album : feed.items) {
if (album.title.equals(EDApplication.ED_ALBUM_NAME)) {
hasEDAlbum = true;
break;
}
}
But - if instead I have:
public class EDAlbum {
#Key("title")
public String title;
#Key("summary")
public String summary;
#Key("gphoto:access")
public String access;
#Key("category")
public EDCategory category = EDCategory.newKind("album");
}
Then the feed has an empty collection - i.e. the parser does not know that this is Atom (my guess).
I can live with the android:title in my classes - I don;t get it, but it works.
The problem is that I can't get the POST to wok (to create the album). This code is:
EDAlbum a = new EDAlbum();
a.access = "public";
a.title = EDApplication.ED_ALBUM_NAME;
a.summary = c.getString(R.string.ed_album_summary);
AtomContent content = new AtomContent();
content.entry = a;
content.namespaceDictionary = EDApplication.getNamespaceDictionary();
PicasaUrl url = PicasaUrl.relativeToRoot("feed/api/user/default");
HttpRequest request = EDApplication.postRequest(url, content);
HttpResponse res = request.execute();
The transport and namespace are:
private static final HttpTransport transport = new ApacheHttpTransport(); // my libraries don;t include GoogleTransport.
private static HttpRequestFactory createRequestFactory(final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
AtomParser parser = new AtomParser();
parser.namespaceDictionary = getNamespaceDictionary();
request.addParser(parser);
}
});
}
public static XmlNamespaceDictionary getNamespaceDictionary() {
if (nsDictionary == null) {
nsDictionary = new XmlNamespaceDictionary();
nsDictionary.set("", "http://www.w3.org/2005/Atom");
nsDictionary.set("atom", "http://www.w3.org/2005/Atom");
nsDictionary.set("exif", "http://schemas.google.com/photos/exif/2007");
nsDictionary.set("gd", "http://schemas.google.com/g/2005");
nsDictionary.set("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#");
nsDictionary.set("georss", "http://www.georss.org/georss");
nsDictionary.set("gml", "http://www.opengis.net/gml");
nsDictionary.set("gphoto", "http://schemas.google.com/photos/2007");
nsDictionary.set("media", "http://search.yahoo.com/mrss/");
nsDictionary.set("openSearch", "http://a9.com/-/spec/opensearch/1.1/");
nsDictionary.set("xml", "http://www.w3.org/XML/1998/namespace");
}
return nsDictionary;
}
If I use
#Key("title")
public String title;
then I get an exception that it does not have a default namespace:
W/System.err( 1957): java.lang.IllegalArgumentException: unrecognized alias: (default)
W/System.err( 1957): at com.google.common.base.Preconditions.checkArgument(Preconditions.java:115)
W/System.err( 1957): at com.google.api.client.xml.XmlNamespaceDictionary.getNamespaceUriForAliasHandlingUnknown(XmlNamespaceDictionary.java:288)
W/System.err( 1957): at com.google.api.client.xml.XmlNamespaceDictionary.startDoc(XmlNamespaceDictionary.java:224)
and if I use
#Key("atom:title")
public String title;
then it does serialize but each element has the atom: prefix and the call fails - when I to a tcpdump on it I see something like
.`....<? xml vers
ion='1.0 ' encodi
ng='UTF- 8' ?><at
om:entry xmlns:a
tom="htt p://www.
w3.org/2 005/Atom
"><atom: category
scheme= "http://
schemas. google.c
om/g/200 5#kind"
term="ht tp://sch
emas.goo gle.com/
photos/2 007#albu
m" /><at om:gphot
o:access >public<
/atom:gp hoto:acc
....
What do I need to do different in order to use
#Key("title")
public String title;
and have both the GET and the POST manage the namespace?
It looks you are adding either duplicate dictonary keys or keys that are not understood by the serializer.
Use the following instead.
static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary()
.set("", "http://www.w3.org/2005/Atom")
.set("activity", "http://activitystrea.ms/spec/1.0/")
.set("georss", "http://www.georss.org/georss")
.set("media", "http://search.yahoo.com/mrss/")
.set("thr", "http://purl.org/syndication/thread/1.0");
Removing the explicit set for the "atom" item namespace solved this issue for me.

Resources