I'm using a REST api that requires a few fields to be set. My application should always set some of the fields to the same value. Is it possible to make those values "hard coded" in the interface with the feign definition (or somewhere else)?
My feign declaration looks like the example. Let's say I always want to use the same agent from this application. Is that doable?
#RequestLine("POST /files")
#Headers("Content-Type: application/json")
default FileMetadata addFile(#Param("file_name") String fileName,
#Param("agent") String agent,
#Param("md5") String md5,
#Param("file_type") String fileType) {
return new FileMetadata.Builder().build();
}
You have different alternatives, in the case you are using the Spring Annotations you could use defaultValue annotation property to determine the default value
#RequestParam(value = "agent", required = false, defaultValue = "AnyAgent") String agent
But in the case you are using the netflix-feign annotations, and seems it is what you are doing, you would need to add in the path, i.e. in the #RequestLine annotation:
static final String DEFAULT_VALUE_AGENT = "agent";
#RequestLine("POST /files?agent=" + DEFAULT_VALUE_AGENT)
....
But if you are refering to Body POST, you could do it using annotation #Body:
#RequestLine("POST /files")
#Body("%7B\"file_name\": \"{user_name}\", \"agent\": \"" + DEFAULT_VALUE_AGENT +"\"%7D") //... other params..
....
Edit
Based on the Github repository documentation, if you are using Java 8, you could do a default method which calls the other method with a param constant. Just like next
#RequestLine("POST /files")
#Headers("Content-Type: application/json")
FileMetadata addFile(#Param("file_name") String fileName,
#Param("agent") String agent,
#Param("md5") String md5,
#Param("file_type") String fileType);
#RequestLine("POST /files")
#Headers("Content-Type: application/json")
default FileMetadata addFileConstant(#Param("file_name") String fileName,
#Param("md5") String md5,
#Param("file_type") String fileType) {
addFile(fileName, "constant_value", md5, fileType);
}
Personally, I think is more flexible than the other options I proposed before. And the idea works in POST Body as well if they are params.
I had the same situation.
The problem is solved as follows:
added this to the main api on the server that I was knocking on (i.e., on the receiving side, and not on the sending side request).
for example:
#RequestParam(value = "code", required = false, defaultValue = "AAA") String code
Required is required = false (instead of required = true).
Related
I cannot test Controller by using mockMvc in spring-test. I want to know right way to test API with #RequestPart.
The method to test is this.
#RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseEntity<Object> replaceFile(
#RequestPart("files") Map<String, Object> files,
#RequestPart("fileKey") String fileKey)
And to test I build a mock request like below.
MockMultipartFile blob = new MockMultipartFile("files", files.getBytes());
MockMultipartFile key = new MockMultipartFile("fileKey", fileKey.getBytes());
mockMvc.perform(fileUpload("/")
.file(blob)
.file(key))
.andDo(print())
.andExpect(status().isOk());
As you can see, I used fileUpload. But in past time, I tried to use post with content or requestAttr because all of them didn't work.
I think current code is the closest to answer among I tried, but can't get closer anymore.
The weird thing is, the real in-use API is almost same with them.
In client side, user sent a new FormData() object to request and server can get data properly.
The server side code is below.
#RequestMapping(value = "/api/{variable}", method = RequestMethod.POST)
public ResponseEntity<Object> apiMethod(
#PathVariable int variable,
#RequestPart("dto1") DTO1 dto1,
#RequestPart("dto2") DTO2 dto2,
#RequestPart("file") Map<String, Object> file)
"file" part consists of "file name" key and its blob value encoded base64.
For example, {"hello.txt": "SGVsbG8gd29ybGQh"}
What I want to know - Right way to test API with #RequestPart
What I tried
method - fileUpload / data - file, content, requestAttr but they send null.
method - post / data - file, content, requestAttr but they throw MultipartException.
Restriction - Cannot use multipart because the system is using a low version of Spring.
Thanks!
Thanks to #borino who commented to my question, I got the clue of problem.
It is certain to use fileUpload for testing API with #RequestPart arguments.
In that case, it caused from content type.
I declared MockMultipartFile without contentType.
MockMultipartFile blob = new MockMultipartFile("files", files.getBytes());
MockMultipartFile key = new MockMultipartFile("fileKey", fileKey.getBytes());
But the arguments of API have a type, Map<String,Object> and String each.
As #borino said to me, I changed constructor of MockMultipartFile to make sure contentType, and it works!
MockMultipartFile blob = new MockMultipartFile("files", "", "application/json", files.getBytes());
MockMultipartFile key = new MockMultipartFile("fileKey", "", "application/json", fileKey.getBytes());
Just add contentType when you have a problem like me. Thanks!
This is my ASHX File where I am catching multiple parameters from request URL using httpcontext, and it is working properly but when I am including a Hash(#) value in Text parameter through the URL. It is not taking the value of FLOW which is another parameter(next to Text parameter).
So it is working for:
http://localhost:10326/ussd.ashx?user=MSL&pass=MSL663055&tid=65506&msisdn=8801520101525&text=***3333**&flow=begin&momt=mo
And it is not working for:
http://localhost:10326/ussd.ashx?user=MSL&pass=MSL663055&tid=65506&msisdn=8801520101525&text=***3333#**&flow=begin&momt=mo
My ASHX files:
public void ProcessRequest(HttpContext context)
{
HttpRequest httpRequest = context.Request;
string user = httpRequest.QueryString["user"].ToString();
string pass = httpRequest.QueryString["pass"].ToString();
string tid = httpRequest.QueryString["tid"].ToString();
string msisdn = httpRequest.QueryString["msisdn"].ToString();
string text = httpRequest.QueryString["text"].ToString();
flow = httpRequest.QueryString["flow"].ToString();
HttpContext.Current.Session["user"] = user;
HttpContext.Current.Session["pass"] = pass;
HttpContext.Current.Session["tid"] = tid;
HttpContext.Current.Session["msisdn"] = msisdn;
HttpContext.Current.Session["text"] = text;
HttpContext.Current.Session["flow"] = flow;
You need to URI encode your parameter values before they are added to the URL. This way the server will not get confused by unsafe characters such as '#' which has its own meaning when included as part of a URL. See RFC 3986 Section 2.
See Encode URL in JavaScript as an example of how to encode the sent data using JavaScript. Whatever is sending the data in the URL will need to do the encoding. There is not much you can do once the request has reached the server. Without knowing how your URL is being created, I can't offer much more.
In short: the problem is with your client code not your ASHX file.
I have a query string that looks something like this:
"somename1=123&QueryString=PlaceHolder%3dNothing%26anotherid%3dsomevalue&somename=somevalue"
but I want the query string to be something like the query string below and replace the whole query string with the updated one is there any way to do that without redirection?
"somename1=somevalue1&PlaceHolder=Nothing&somename2=somevalue2&somename3=somevalue3"
basically need to remove:
"QueryString=" with empty string
"%3d" with "&"
"%26" with "="
So far I've done is:
string strQueryString = Request.QueryString.ToString();
if (strQueryString.Contains("QueryString="))
{
strQueryString = strQueryString.Replace("QueryString=", "");
if (strQueryString.Contains("%26")) strQueryString = strQueryString.Replace("%26", "&");
if (strQueryString.Contains("%3d")) strQueryString = strQueryString.Replace("%3d", "=");
string x = strQueryString;
}
and:
// reflect to readonly property
PropertyInfo isreadonly = typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
// make collection editable
isreadonly.SetValue(this.Request.QueryString, false, null);
if (this.Request.QueryString.ToString().Contains("QueryString="))
{
this.Request.QueryString.ToString().Replace("QueryString=", "");
if (this.Request.QueryString.ToString().Contains("%26")) this.Request.QueryString.ToString().Replace("%26", "&");
if (this.Request.QueryString.ToString().Contains("%3d")) this.Request.QueryString.ToString().Replace("%3d", "=");
string x = this.Request.QueryString.ToString();
}
// make collection readonly again
isreadonly.SetValue(this.Request.QueryString, true, null);
The second part of the code is not replacing the characters and I don't know how after removing all character or replacing them change the query string to new query string.
Any help is greatly appreciated.
Changing the query string of the current request is not supported. Using private Reflection to edit some in-memory state will most likely break ASP.NET because it assumes that the query string is immutable. The only way to change the query string is to issue a new request, either by doing a redirect, or by doing a sort of sub-request, such as by making a new HTTP request to the same page but with a different query string.
May I suggest a not very well known built in key/value dictionary, Context.Items.
With this you very like get a better performance than toggle the readonly QueryString object, and it also last throughout a request so you can share it between module, handlers, etc.
Create
string strQueryString = Request.QueryString.ToString();
if (strQueryString.Contains("QueryString="))
{
HttpContext.Current.Items("qs") = strQueryString.Replace("QueryString=", "").Replace("%26", "&").Replace("%3d", "=");
}
Use
string x = HttpContext.Current.Items("qs_d").ToString();
Side note: I shortened you code some, as there is no need to first check if anything contains and if so, replace, just run replace, it will be faster
I created a custom dispatcher to handle versioning that uses a customer media type. It looks something like this:
application/vnd.mycompany.myapi-v1+json
The extraction of the version number in order to select the correct controller is all up and working, but being new to MVC, I am not sure how to set the response format. What we want to do is set the response format to match the request. So in this example, the response will be in json. Now I assume I'm going to have to extract that from this content type as well which is fine, but could someone give me an example of how i set the response format of this request in MVC4 assuming I have already created the method which will extract the format as a string?
private string GetResponseFormat(){
//some shennanigans here
}
P.S. the reason for not having the client use the accept header during the request is that there are already clients out there that are using our old service which would set the accept header to match the request.
You can also use Content method to return custom response type:
string responseType = GetResponseFormat();
...
switch(responseType){
case "json":
string json = "yourJSON";
return Content(json, "application/json");
case "xml":
string xml = "yourXML";
return Content(xml, "text/xml");
default:
string plaintxt = "yourPlaintext";
return Content(plaintxt, "text/plain"):
}
I was able to clear the existing Accept header and add to it:
private void SetResponseFormatToRequestFormat(HttpRequestMessage request)
{
// figure out what the request format was
_contentTypeHeader = request.Content.Headers.ContentType.ToString();
if(_contentTypeHeader.Contains("xml")) _contentType = "application/xml";
if (_contentTypeHeader.Contains("json")) _contentType = "application/json";
// set response format to the same as the request format
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(_contentType));
}
I don't know if I'm just being overly hopeful, but is there a way to hide the query string returned in the URL?
The scenario I am in is where I have page1.aspx redirecting a command to an outside server via a post, and it returns it to page2.aspx. The only problem I have with this, is that the querystring of the returned variables are still left in the URL.
I just want to hide the ugly string/information from the common user. So is there a way to edit and reload that in the pageload method or do I just have to save the variables on a middleman page and then hit page 2.
What is the origin of these querystring variables? Can you not submit all data as POST data, so that there is no querystring?
You could possibly also use
Context.RewritePath("/foo.aspx")
Here's a link to a ScottGu blog post about URL rewriting.
http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx
Awhile back I made some http encoding encrypt/decrypt methods for this purpose. Sometimes in asp.net you need to use the query string, but you also need the end user to not know the value. What I do is base 64 encode, encrypt the value, hash the value based on my private key, and stick them together with a -. On the other side I check the left side hash to verify authenticity, and decrypt the right side. One really nice gotcha is that + (which is a valid base64 string value) is equal to space in html encoding, so I take that into account in the decrypt.
The way I use this is add the encrypted value to the query string, and then decrypt it on the other side
private const string KEY = "<random value goes here>";
public static string EncryptAndHash(this string value)
{
MACTripleDES des = new MACTripleDES();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.UTF8.GetBytes(KEY));
string encrypted = Convert.ToBase64String(des.ComputeHash(Encoding.UTF8.GetBytes(value))) + '-' + Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
return HttpUtility.UrlEncode(encrypted);
}
/// <summary>
/// Returns null if string has been modified since encryption
/// </summary>
/// <param name="encoded"></param>
/// <returns></returns>
public static string DecryptWithHash(this string encoded)
{
MACTripleDES des = new MACTripleDES();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.UTF8.GetBytes(KEY));
string decoded = HttpUtility.UrlDecode(encoded);
// in the act of url encoding and decoding, plus (valid base64 value) gets replaced with space (invalid base64 value). this reverses that.
decoded = decoded.Replace(" ", "+");
string value = Encoding.UTF8.GetString(Convert.FromBase64String(decoded.Split('-')[1]));
string savedHash = Encoding.UTF8.GetString(Convert.FromBase64String(decoded.Split('-')[0]));
string calculatedHash = Encoding.UTF8.GetString(des.ComputeHash(Encoding.UTF8.GetBytes(value)));
if (savedHash != calculatedHash) return null;
return value;
}
I don't like this approach, but it will work.
Once you know you are where you need to be you can Response.Redirect to the same page and they will be gone.
It preserves Query String and Form Variables (optionally). It doesn’t show the real URL where it redirects the request in the users web browser. Server.Transfer happens without the browser knowing anything. The browser requests a page, but the server returns the content of another.
protected void btnServer_Click(object sender, EventArgs e)
{
Server.Transfer("~/About.aspx?UserId=2");
}