I am using UriBuilder to remove a parameter from a URI:
public static URI removeParameterFromURI(URI uri, String param) {
UriBuilder uriBuilder = UriBuilder.fromUri(uri);
return uriBuilder.replaceQueryParam(param, "").build();
}
public static String removeParameterFromURIString(String uriString, String param) {
try {
URI uri = removeParameterFromURI(new URI(uriString), param);
return uri.toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
The above sort of works and modifies:
http://a.b.c/d/e/f?foo=1&bar=2&zar=3
… into:
http://a.b.c/d/e/f?bar=&foo=1&zar=3
But it has the following issues:
It messes up the order of the parameters. I know that the order is not relevant but it still bothers me.
it doesn't fully remove the parameter, it just sets its value to the empty string. I would prefer is the parameter is completely removed from the query string.
Is there some standard or commonly used library that can achieve the above neatly without having to parse and hack the query string myself?
In Android, without import any library.
I write a util method inspired by this answerReplace query parameters in Uri.Builder in Android?(Replace query parameters in Uri.Builder in Android?)
Hope can help you. Code below:
public static Uri removeUriParameter(Uri uri, String key) {
final Set<String> params = uri.getQueryParameterNames();
final Uri.Builder newUri = uri.buildUpon().clearQuery();
for (String param : params) {
if (!param.equals(key)) {
newUri.appendQueryParameter(param, uri.getQueryParameter(param));
}
}
return newUri.build();
}
Using httpclient URIBuilder would be much cleaner if you can.
public String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
for (Iterator<NameValuePair> queryParameterItr = queryParameters.iterator(); queryParameterItr.hasNext();) {
NameValuePair queryParameter = queryParameterItr.next();
if (queryParameter.getName().equals(parameterName)) {
queryParameterItr.remove();
}
}
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}
If you are on Android and want to remove all query parameters, you can use
Uri uriWithoutQuery = Uri.parse(urlWithQuery).buildUpon().clearQuery().build();
Using streams and URIBuilder from httpclient it would look like this
public String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams()
.stream()
.filter(p -> !p.getName().equals(parameterName))
.collect(Collectors.toList());
if (queryParameters.isEmpty()) {
uriBuilder.removeQuery();
} else {
uriBuilder.setParameters(queryParameters);
}
return uriBuilder.build().toString();
}
To fully remove the parameter, you can use
public static URI removeParameterFromURI(URI uri, String param) {
UriBuilder uriBuilder = UriBuilder.fromUri(uri);
return uriBuilder.replaceQueryParam(param, (Object[]) null).build();
}
The following piece of code worked for me:
Code:
import java.util.Arrays;
import java.util.stream.Collectors;
public class RemoveURL {
public static void main(String[] args) {
final String remove = "password";
final String url = "http://testdomainxyz.com?username=john&password=cena&password1=cena";
System.out.println(url);
System.out.println(RemoveURL.removeParameterFromURL(url, remove));
}
public static String removeParameterFromURL(final String url, final String remove) {
final String[] urlArr = url.split("\\?");
final String params = Arrays.asList(urlArr[1].split("&")).stream()
.filter(item -> !item.split("=")[0].equalsIgnoreCase(remove)).collect(Collectors.joining("&"));
return String.join("?", urlArr[0], params);
}
}
Output
http://testdomainxyz.com?username=john&password=cena&password1=cena
http://testdomainxyz.com?username=john&password1=cena
Based on the suggestion by JB Nizzet, this is what I ended up doing (I added some extra logic to be able to assert whether I expect the parameter to be present, and if so, how many times):
public static URI removeParameterFromURI(URI uri, String parameter, boolean assertAtLeastOneIsFound, Integer assertHowManyAreExpected) {
Assert.assertFalse("it makes no sense to expect 0 or less", (assertHowManyAreExpected!=null) && (assertHowManyAreExpected<=0) );
Assert.assertFalse("it makes no sense to not assert that at least one is found and at the same time assert a definite expected number", (!assertAtLeastOneIsFound) && (assertHowManyAreExpected!=null) );
String queryString = uri.getQuery();
if (queryString==null)
return uri;
Map<String, List<String>> params = parseQuery(queryString);
Map<String, List<String>> paramsModified = new LinkedHashMap<>();
boolean found = false;
for (String key: params.keySet()) {
if (!key.equals(parameter))
Assert.assertNull(paramsModified.put(key, params.get(key)));
else {
found = true;
if (assertHowManyAreExpected!=null) {
Assert.assertEquals((long) assertHowManyAreExpected, params.get(key).size());
}
}
}
if (assertAtLeastOneIsFound)
Assert.assertTrue(found);
UriBuilder uriBuilder = UriBuilder.fromUri(uri)
.replaceQuery("");
for (String key: paramsModified.keySet()) {
List<String> values = paramsModified.get(key);
uriBuilder = uriBuilder.queryParam(key, (Object[]) values.toArray(new String[values.size()]));
}
return uriBuilder.build();
}
public static String removeParameterFromURI(String uri, String parameter, boolean assertAtLeastOneIsFound, Integer assertHowManyAreExpected) {
try {
return removeParameterFromURI(new URI(uri), parameter, assertAtLeastOneIsFound, assertHowManyAreExpected).toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private static Map<String, List<String>> parseQuery(String queryString) {
try {
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
final String[] pairs = queryString.split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.name()) : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new ArrayList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.name()) : null;
query_pairs.get(key).add(value);
}
return query_pairs;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
You can use simpler method from Collection based on #Flips solution:
public String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
queryParameters.removeIf(param ->
param.getName().equals(parameterName));
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}
I am not sure if there is some library to help, but I would just split the string on "?" and take the second half and split it on "&". Then I would rebuild the string accordingly.
public static void main(String[] args) {
// TODO code application logic here
System.out.println("original: http://a.b.c/d/e/f?foo=1&bar=2&zar=3");
System.out.println("new : " + fixString("http://a.b.c/d/e/f?foo=1&bar=2&zar=3"));
}
static String fixString(String original)
{
String[] processing = original.split("\\?");
String[] processing2ndHalf = processing[1].split("&");
return processing[0] + "?" + processing2ndHalf[1] + "&" + processing2ndHalf[0] + "&" + processing2ndHalf[2];
}
Output:
To remove a paramater just remove it from the return string.
public static String removeQueryParameter(String url, List<String> removeNames) {
try {
Map<String, String> queryMap = new HashMap<>();
Uri uri = Uri.parse(url);
Set<String> queryParameterNames = uri.getQueryParameterNames();
for (String queryParameterName : queryParameterNames) {
if (TextUtils.isEmpty(queryParameterName)
||TextUtils.isEmpty(uri.getQueryParameter(queryParameterName))
|| removeNames.contains(queryParameterName)) {
continue;
}
queryMap.put(queryParameterName, uri.getQueryParameter(queryParameterName));
}
// remove all params
Uri.Builder uriBuilder = uri.buildUpon().clearQuery();
for (String name : queryMap.keySet()) {
uriBuilder.appendQueryParameter(name, queryMap.get(name));
}
return uriBuilder.build().toString();
} catch (Exception e) {
return url;
}
}
#TTKatrina's answer worked for me, but I need to remove query param from fragment too. So extended that for fragment and came up with this.
fun Uri.removeQueryParam(key: String): Uri {
//Create new Uri builder with no query params.
val builder = buildUpon().clearQuery()
//Add all query params excluding the key we don't want back to the new Uri.
queryParameterNames.filter { it != key }
.onEach { builder.appendQueryParameter(it, getQueryParameter(it)) }
//If query param is in fragment, remove from it.
val fragmentUri = fragment?.toUri()
if (fragmentUri != null) {
builder.encodedFragment(fragmentUri.removeQueryParam(key).toString())
}
//Now this Uri doesn't have the query param for [key]
return builder.build()
}
Related
I have two url calls in one method that is in addnewMap() - one is the buildGetSubtenantsURL and the other is buildGetAssetsURL
public void addNewMap(MapDTO mapDTO) {
log.info("going to add the map data into db");
if (mapRepository.existsMapWithMapName(mapDTO.getMapName()))
throw new BadRequestException("Map with map name " + mapDTO.getMapName()
+ " already exists. Please provide a different map name.");
Map<String, String> subtenantInfoMap = new HashMap<>();
Maps mapEntity = new Maps();
String iottenant = mapDTO.getTenant();
String subtenantsURL = buildGetSubtenantsURL(null);
String subTenantsResponse = getSubtenants(subtenantsURL,iottenant);
JSONObject subTenant = getSubtenantName(subTenantsResponse);
checkForMultiplePagesSubtenants(subTenantsResponse, subtenantInfoMap,iottenant);
if(subtenantInfoMap.get(mapDTO.getSubtenantName()) != null) {
mapEntity = Maps.builder().subtenant(subtenantInfoMap.get(mapDTO.getSubtenantName()).toString()).build();
}
else {
throw new DataNotFoundException(SUBTENANT_DOESNT_EXIST);
}
String SubtenantId = subtenantInfoMap.get(mapDTO.getSubtenantName());
UriComponents assetsURL = buildGetAssetsURL(iottenant,SubtenantId);
String assetsResponse = getAssets(assetsURL, iottenant);
String mindsphereAssetId = getAssetId(assetsResponse);
if(mindsphereAssetId.isEmpty()) {
throw new DataNotFoundException(ASSET_ID_DOESNT_EXIST);
}
else {
mapEntity = Maps.builder().mindsphereAssetId(mindsphereAssetId).build();
}
mapEntity = Maps.builder().mapName(mapDTO.getMapName()).displayName(getDisplayName(mapDTO))
.description(Objects.nonNull(mapDTO.getDescription()) ? mapDTO.getDescription() : null)
.tenant(getTenantNameForMapDTO(mapDTO)).mindsphereAssetId(mindsphereAssetId).subtenant(subtenantInfoMap.get(mapDTO.getSubtenantName()).toString())
.mapLocation(mapDTO.getMapLocation()).operator(mapDTO.getOperator()).recipeName(mapDTO.getRecipeName()).subtenantName(mapDTO.getSubtenantName())
.createdBy(getUserEmail()).createdAt(new Timestamp(System.currentTimeMillis()))
.build();
Maps createdMap = mapRepository.saveAndFlush(mapEntity);
addStationsMappingforNewMap(createdMap);
}
I have written the test case for the above method as:
#Test
public void addNewMap() {
map = Maps.builder().mapId(1l).mapName("testMap").displayName("Map Test").mindsphereAssetId("a0609ebf2eb7400da8a5fd707e7f68b7").mapLocation("hyd").operator("operator").recipeName("recipe").subtenantName("NSTI").tenant("ctlbrdev").subtenant("9b04027dde5fbd047073805ab8c1c87c")
.tenant(Tenant).build();
maps = Arrays.asList(map);
mapDTO = MapDTO.builder().mapId(1l).mapName("testMap").displayName("Map Test").subtenantName("NSTI").mapLocation("hyd").recipeName("recipe").operator("operator").description("description")
.tenant("ctlbrdev").build();
ReflectionTestUtils.setField(mapService, "mindsphereBaseURL", MindsphereBaseURL);
ReflectionTestUtils.setField(mapService, "mindsphereSubtenantsURL", mindsphereSubtenantsURL);
ReflectionTestUtils.setField(mapService, "mindsphereAssetsURL", mindsphereAssetsURL);
when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(new ResponseEntity<String>(entityDtoCreaters.getSubtenant(),HttpStatus.OK));
when(tokenCaching.retrieveHeadersContainingTechToken("ctblrdev")).thenReturn(new HttpHeaders());
when(mapRepository.existsMapWithMapName(any())).thenReturn(false);
//doReturn(Tenant).when(mapService).getTenantName();
doReturn(EMAIL).when(mapService).getUserEmail();
when(mapRepository.saveAndFlush(any())).thenReturn(map);
when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(new ResponseEntity<String>(entityDtoCreaters.getSubtenant(),HttpStatus.OK));
Map<String, String> subtenantInfoMap = new HashMap<>();
subtenantInfoMap.get(mapDTO.getSubtenantName());
mapService.addNewMap(mapDTO);
It is not covering the getAssets() method, hence not covering the whole method. how can I achieve this?
I want to fetch header from csv file . If I am not use this skipLines then I will get header at 0 index array . But I want to fetch header directly using HeaderColumnNameMappingStrategy but it will not work with my code.
I also want to validate header column list ( like csv had not allowed to contain extra column)
I had also check this How to validate the csv headers using opencsv but it was not helpful to me.
#SuppressWarnings({ "unchecked", "rawtypes" })
public Map<String, Object> handleStockFileUpload(MultipartFile file, Long customerId) {
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("datamap", "");
responseMap.put("errormap", "");
responseMap.put("errorkeys", "");
List<Map<String, Integer>> list = new ArrayList<>();
List<StockCsvDTO> csvStockList = new ArrayList<>();
try {
String fileName = new SimpleDateFormat("yyyy_MM_dd_HHmmss").format(new Date()) + "_" + file.getOriginalFilename();
responseMap.put("filename", fileName);
File stockFile = new File(productsUploadFilePath + fileName);
stockFile.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(stockFile);
fos.write(file.getBytes());
fos.close();
CsvTransfer csvTransfer = new CsvTransfer();
ColumnPositionMappingStrategy ms = new ColumnPositionMappingStrategy();
ms.setType(StockCsv.class);
Reader reader = Files.newBufferedReader(Paths.get(productsUploadFilePath + fileName));
CSVReader csvReader = new CSVReader(reader);
CsvToBean cb = new CsvToBeanBuilder(reader)
.withType(StockCsv.class)
.withMappingStrategy(ms)
.withSkipLines(1)
.build();
csvTransfer.setCsvList(cb.parse());
reader.close();
csvStockList = csvTransfer.getCsvList();
} catch (Exception e) {
e.printStackTrace();
responseMap.put("status", "servererror");
}
responseMap.put("datamap", csvStockList);
return responseMap;
}
I found the following solution:
Use #CsvBindByName with HeaderColumnNameMappingStrategy,e.g. annotate your bean properties with #CsvBindByName:
public static class HollywoodActor {
private int id;
#CsvBindByName(column = "First Name")
private String firstName;
#CsvBindByName(column = "Last Name")
private String lastName;
// getter / setter
}
Add a method like this:
public class CsvParser {
public <T> ParseResult<T> parseByPropertyNames(Reader csvReader, Class<T> beanClass) throws IOException {
CSVReader reader = new CSVReaderBuilder(csvReader).withCSVParser(new
CSVParserBuilder().build()).build();
CsvToBean<T> bean = new CsvToBean();
HeaderColumnNameMappingStrategy<T> mappingStrategy = new HeaderColumnNameMappingStrategy();
mappingStrategy.setType(beanClass);
bean.setMappingStrategy(mappingStrategy);
bean.setCsvReader(reader);
List<T> beans = bean.parse();
return new CsvParseResult<>(mappingStrategy.generateHeader(), beans);
}
and also don't forget to add public class ParseResult
public class ParseResult <T> {
private final String[] headers;
private final List<T> lines;
// all-args constructor & getters
}
Use then use them in your code:
String csv = "Id,First Name,Last Name\n" + "1, \"Johnny\", \"Depp\"\n" + "2, \"Al\", \"Pacino\"";
CsvParseResult<HollywoodActor> parseResult = parser
.parseByPropertyNames(new InputStreamReader(new ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8), HollywoodActor.class)));
From ParseResult.headers you can get actual headers from which were in your .csv file. Just compare them with what's expected.
Hope that helps!
Here I was comparing my csvHeader with originalHeader:
List<String> originalHeader = fileUploadUtility.getHeader(new StockCsv());
List<String> invalidHeader = csvHeader.stream().filter(o -> (originalHeader.stream().filter(f -> f.equalsIgnoreCase(o)).count()) < 1).collect(Collectors.toList());
if(null != invalidHeader && invalidHeader.size() > 0 && invalidHeader.toString().replaceAll("\\[\\]", "").length() > 0) {
msg = "Invalid column(s) : " + invalidHeader.toString().replace(", ]", "]") + ". Please remove invalid column(s) from file.";
resultMap.put(1, msg);
}
public List<String> getHeader(T pojo) {
// TODO Auto-generated method stub
final CustomMappingStrategy<T> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType((Class<? extends T>) pojo.getClass());
String header[] = mappingStrategy.generateHeader();
List<String> strHeader = Arrays.asList(header);
return strHeader;
}
Here is an alternative to your present problem.First, define what you expect your headers to look like. For example:
public static final ArrayList<String> fileFormat = new ArrayList<> (Arrays.asList("Values1", "Values2", "Values3", "Values4"));
Now, write a method to return custom errors if any exist:
public String validateCsvFileDetails(MultipartFile file, Set<String> requiredHeadersArray) {
Set<String> errors = new HashSet<>();
try {
InputStream stream = file.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String headerLine = reader.readLine();
if (Objects.isNull(headerLine))
return "The file has no headers, please ensure it has the correct upload format";
List<String> headersInFileList;
String[] headersInFileArray;
if (headerLine.contains(",")) {
headersInFileArray = StringUtils.split(headerLine, ",");
headersInFileList = Arrays.asList(headersInFileArray);
} else//the headerline has only one headerfield
{
headersInFileList = Collections.singletonList(headerLine);
}
for (String header : requiredHeadersArray) {
if (!headersInFileList.contains(header))
errors.add("The file has the wrong header format, please ensure " + header + " header is present");
}
//if there are errors, return it
if (!errors.isEmpty())
return sysUtils.getStringFromSet(errors);
//Ensure the csv file actually has values after the header, but don't read beyond the first line
String line;
int counter = 0;
while ((line = reader.readLine()) != null) {
counter++;
if (counter > 0)
break;
}
//if line is null return validation error
if (Objects.isNull(line))
return "Cannot upload empty file";
} catch (Exception e) {
logger.error(new Object() {
}.getClass().getEnclosingMethod().getName(), e);
return "System Error";
}
return null;
}
Now you can validate you file headers as follows:
String errors = validateCsvFileDetails(file, new HashSet<>(fileFormat));
if (errors != null)
return error
//proceed
Give this a try using captureHeader as a pre-filter:
...
private class CustomHeaderColumnNameMappingStrategy<T> extends HeaderColumnNameMappingStrategy {
private String[] expectedHeadersOrdered = {"Column1", "Column2", "Column3", "Column4", "Column5"};
#Override
public void captureHeader(CSVReader reader) throws IOException, CsvRequiredFieldEmptyException {
String[] actualCsvHeaders = reader.peek();
String actualHeader, expectedHeader;
if (expectedHeadersOrdered.length > actualCsvHeaders.length) {
throw new CsvRequiredFieldEmptyException("Missing header column.");
} else if (expectedHeadersOrdered.length < actualCsvHeaders.length) {
throw new IOException("Unexpected extra header column.");
}
// Enforce strict column ordering with index
// TODO: you might want to employ simple hashMap, List, set, etc. as needed
for (int i=0; i<actualCsvHeaders.length; i++) {
actualHeader = actualCsvHeaders[i];
expectedHeader = expectedHeadersOrdered[i];
if ( ! expectedHeader.equals(actualHeader) ) {
throw new IOException("Header columns mismatch in ordering.");
}
}
super.captureHeader(reader); // Back to default processing if the headers include ordering are as expected
}
}
CustomHeaderColumnNameMappingStrategy yourMappingStrategy = new CustomHeaderColumnNameMappingStrategy<YourPOJO>();
ourMappingStrategy.setType(YourPOJO.class);
try {
pojosFromCsv = new CsvToBeanBuilder<YourPOJO>(new FileReader(csvFile))
.withType(YourPOJO.class)
.withMappingStrategy(yourMappingStrategy)
.build();
pojosFromCsv.stream();
}
Inspired by Using captureHeader in OpenCSV
public class Ussd {
private static Logger logger = LogManager.getLogger(Ussd.class);
private static String query;
public static void ussdMessages(RoutingContext routingContext){
String codeService = routingContext.getBodyAsJson().getString("codeService");
query=getServiceQuery(codeService);
System.out.println("Query : "+query);
}
public static String getServiceQuery(String codeService){
JsonObject params = new JsonObject();
params.put("QUERY", Queries.DB_SELECT_SERVICE_QUERY);
params.put("PARAMS", new JsonArray().add(codeService));
System.out.println(params);
DB.select(params, res -> {
if (res.succeeded()) {
query = res.result().getJsonArray("results").getJsonArray(0).getString(0);
System.out.println(query);
} else {
query = res.cause().getMessage();
}
});
return query;
}
}
The method getServiceQuery returns my exact result and I stock it into static query but after recall inside method ussdMesages, it returns null from the first request using Postman. For the second request, it returns the correct result. I don't know why.
Your getServiceQuery method does async work(DB.select), so in your case you are returning the query before it getting processed. Change the return type of the function from String to Future.
public class Ussd {
private static Logger logger = LogManager.getLogger(Ussd.class);
private static String query;
public static void ussdMessages(RoutingContext routingContext){
String codeService = routingContext.getBodyAsJson().getString("codeService");
getServiceQuery(codeService).setHandler(r -> {
System.out.println("query : "+r.result());
});
}
public static Future<String> getServiceQuery(String codeService){
Future<String> future = Future.future();
JsonObject params = new JsonObject();
params.put("QUERY", Queries.DB_SELECT_SERVICE_QUERY);
params.put("PARAMS", new JsonArray().add(codeService));
System.out.println(params);
DB.select(params, res -> {
if (res.succeeded()) {
query = res.result().getJsonArray("results").getJsonArray(0).getString(0);
System.out.println(query);
future.complete(query);
} else {
future.fail(res.cause().getMessage());
}
});
return future;
}
}
Here is my code snippets of ChannelInitializer#initChannel
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec()
.addLast(new HttpObjectAggregator(65536))
.addLast( new LoggingHandler(LogLevel.INFO))
.addLast(new WebSocketServerProtocolHandler("/chat"))
.addLast(new TextWebSocketFrameToChatMessageDecoder())
.addLast(new UserAccessHandler())
It can be accepted via ws://mydomain/chat, and now I want to parse query string like this ws://mydomain/chat?accesskey=hello
I have looked up WebSocketServerProtocolHandler, but I couldn't find how to get query string.
How can I get(or parse) query string?
Thanks for your help.
Override method: userEventTriggered and handling event HandshakeComplete.
see WebSocketServerProtocolHandshakeHandler
Netty 4.1
public class TextWebSocketFrameToChatMessageDecoder extends SimpleChannelInboundHandler<WebSocketFrame> {
public final static AttributeKey<Map<String,String>> RequestParams = AttributeKey.valueOf("request.params");
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof HandshakeComplete){
HandshakeComplete handshake = (HandshakeComplete)evt;
//http request header
HttpHeaders headers = handshake.requestHeaders();
//http request uri: /chat?accesskey=hello
String uri = handshake.requestUri();
//TODO: parse uri parameters to map ...
Map<String, String> params ;
//put to channel context
ctx.channel().attr(RequestParams).set(params);
}else{
ctx.fireUserEventTriggered(evt);
}
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
//on message
if (frame instanceof TextWebSocketFrame) {
Map<String, String> params = ctx.channel().attr(RequestParams).get();
String accesskey = params.get("accesskey");
System.out.println( accesskey ); //hello
} else {
System.out.println( "Unsupported frame type: " + frame.getClass().getName() );
}
}
}
I have created 3 new classes, copied them.
WebSocketServerProtocolHandler
WebSocketServerProtocolHandshakeHandler
WebSocketProtocolHandler
And in copy of WebSocketServerProtocolHandshakeHandler, added these code
if(!req.getUri().matches(websocketPath)){
ctx.fireChannelRead(msg);
return;
}
String [] splittedUri = req.getUri().split("\\?");
HashMap<String, String> params = new HashMap<String, String>();
if(splittedUri.length > 1){
String queryString = splittedUri[1];
for(String param : queryString.split("&")){
String [] keyValue = param.split("=");
if(keyValue != null && keyValue.length >= 2){
logger.trace("key = {}, value = {}", keyValue[0], keyValue[1]);
params.put(keyValue[0], keyValue[1]);
}
}
}
ctx.channel().attr(AttrKeys.getInstance().params()).set(params);
Now I can accpet multiple uri and use query string well.
I think somebody will need this answer.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I stop HTTP from escaping quotes?
I am creating a JSONObject and sending JSON string to a server in a POST request body.
public String toJson() {
JSONObject filter = new JSONObject();
try {
filter.put("gender", gender.getCode());
filter.put("feature_id", productCategory);
} catch (JSONException e) {
e.printStackTrace();
}
JSONObject filterObject = new JSONObject();
try {
filterObject.put("filter", filter);
} catch (JSONException e) {
e.printStackTrace();
}
return filterObject.toString();
}
So I'm creating a request:
private IJsonExecutorInterface requestExecutorForRelativePathAndParams(String path, WebParams params) throws UnsupportedEncodingException {
HttpPost postRequest = new HttpPost(rootUrl + path);
if(params != null) {
postRequest.setHeader("content-type", params.getContentType());
postRequest.setEntity(params.getFormEntity());
}
// Blah blah
return executor;
}
public IJsonExecutorInterface getProducts(ProductFilter filter, int offset, int limit) throws UnsupportedEncodingException {
WebParams webParams = new WebParams();
webParams.addPair("filter", filter.toJson());
webParams.addPair("offset", String.format("%d", offset));
webParams.addPair("limit", String.format("%d", limit));
return requestExecutorForRelativePathAndParams("products", webParams);
}
// WebParams class
public class WebParams {
private ArrayList<NameValuePair> params;
private String contentType = "application/x-www-form-urlencoded";
public WebParams() {
params = new ArrayList<NameValuePair>();
}
public void addPair(String name, String value) {
params.add(new BasicNameValuePair(name, value));
}
public String getContentType() {
return contentType;
}
public HttpEntity getFormEntity() throws UnsupportedEncodingException {
return new UrlEncodedFormEntity(params);
}
}
I see it in debugger: it's ok.
But on my server I getting something like this:
Array
(
[filter] => {\"gender\":\"w\",\"feature_id\":\"41_7459\"}
[offset] => 0
[limit] => 18
)
The quotes ARE escaped.
I don't want to replace something on the server. replace("\\\"", "\"") in Java doesn't affect on the string.
Looks like your using a UrlEncodedFormEntity, which, according to the docs is 'An entity composed of a list of url-encoded pairs' ([http://developer.android.com/reference/org/apache/http/client/entity/UrlEncodedFormEntity.html]). I've never used this, but it doesn't sound like its what you want, as you are sending data in the post body, not through URL parameters.
I've used the StringEntity class before to send json data via post, although it only encodes a string, not name/value pairs, so you've got to do a bit more work to put the string in a format you want to deal with on your server:
public class WebParams {
private ArrayList<NameValuePair> params;
private String contentType = "application/x-www-form-urlencoded";
public WebParams() {
params = new ArrayList<NameValuePair>();
}
public void addPair(String name, String value) {
params.add(new BasicNameValuePair(name, value));
}
public String getContentType() {
return contentType;
}
public HttpEntity getFormEntity() throws UnsupportedEncodingException {
//TODO: Build a string in what ever format you want.
// This will include the gender & feature_id fields as well as the json
StringBuilder b = new StringBuilder();
for(NameValuePair nvp : params) {
builder.append(nvp.getName()).append('=').append(nvp.getValue()).append(',');
}
//Now that we have a string to send to the server, get your entity!
StringEntity entity = new StringEntity(b.toString());
entity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
return entity;
}
}
Is there a problem to use simple quotes instead of double quotes? Because I think it would solve your problem.