Feign rest client, disable decode on specific interface methods - netflix-feign

Going through the learning curve, and came across this scenario:
Given that 90% of the calls are JSON, added a GSON decoder when building the client. However, there are some method calls in the interface that should support raw return without decoding.
#RequestLine("GET /rest/myrawmethod")
String getRawMethod();
Currently since GSON is added as a decoder, instead of returning the raw string it attempts to decode it (it does look like JSON content, but I want to bypass decoding). I can't seem to find an easy way to disable for specific interface methods when not to use the GSON decoder as the exception.
Thanks!

Saw some references to various approaches, this seems like the best avenue at this time:
#RequestLine("GET /rest/myrawmethod")
feign.Response getRawMethod();
Then when you go to parse the response, use something like:
feign.codec.Decoder dc = new Decoder.Default();
String strresponse = dc.decode(myfeignresponse, String.class); //wrapped with exception handling
Good way to prototype in scenarios where you don't have anything around the REST payload, only the method calls...or want to do something more exotic (like use the feign.Response streaming methods).

Try making a custom Decoder like this:
class StringHandlingDecoder implements Decoder {
private final Decoder defaultDecoder;
StringHandlingDecoder(Decoder defaultDecoder) {
this.defaultDecoder = defaultDecoder;
}
#Override
public Object decode(Response response, Type type) throws IOException, FeignException {
if (type == String.class) {
return new StringDecoder().decode(response, type);
} else {
return this.defaultDecoder.decode(response, type);
}
}
}
Then build your client like this:
Feign.builder()
.decoder(new StringHandlingDecoder(new GsonDecoder()))
.build();

Related

Around Advice not working when dependent on response from other REST service

I am working with Spring AOP to define a common fallback method instead of duplicating code.
I used #Around as I have to return the object from Aspect.I am trying to decide #Around advice depending on the response returned,but not able to do so.
Here is my controller:
#RequestMapping(value = "/add/employee", method = RequestMethod.GET)
public EmployeeResponse addEmployee(#RequestParam("name") String name, #RequestParam("empId") String empId) {
EmployeeResponse employeeResponse=employeeService.createEmployee(name, empId);
return employeeResponse;
}
createEmployee in the service class is used to call another endpoint to insert some data.I want to decide my advice based on the employeeResponse but not able to do so.
I tried #AfterReturning also,but I can't return the object if I use that.
Below is my aspect class:
#Around(value = "execution(* com.test.service.EmployeeService.*(..)) and args(name,empId)")
public Object getAllAdvice2(ProceedingJoinPoint pjp, String name,String empId) throws Throwable {
System.out.println("Inside Aspect");
Object[] arguments = pjp.getArgs();
if (!checkForPath()) {
return pjp.proceed();
}
System.out.println("Call Second path please!!");
return arguments;
}
private boolean checkForPath() {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getResponse();
return response.getStatus()==501?true:false;
}
}
I did use HttpServletResponse and RequestContextHolder to get the context but seems it will take the present context i.e. "/add/employee".
How can I return the actual status from the checkForPath () (since I don't need to call pjp.proceed for every status code returned) so that I can execute the line System.out.println("Call Second path please!!"); depending on my error code.
Can anyone pls suggest where it is going wrong?
Your aspect code is quite chaotic and does not make much sense:
You are trying to check for a response before calling proceed(), as R.G said. Use something like EmployeeResponse response = (EmployeeResponse) proceed() instead, inspect the response and then decide what to do next.
You already bind the method parameters to name and empId, there is no need to use pjp.getArgs().
return arguments does not make sense because you ought to return an EmployeeResponse object (either the original result or another one), not the array of method arguments.

Return a data object with a BadRequestResult / BadRequestErrorMessageResult

I'd like to return a data object that contains the details of the error with a BadRequestErrorMessageResult or BadRequestErrorMessageResult object like so:
public IHttpActionResult Action(Model model)
{
var validationResult = model.Validate();
if (validationResult.Successful)
{
// this one's okay; it supports sending data with a 200
return Ok(validationResult);
}
else
{
// However, how do I return a custom data object here
// like so?
// No such overload, I wish there was
// return BadRequest(validationResult);
}
}
The only three overloads of the ApiController.BadRequest() method are:
1. BadRequest();
2. BadRequest(string message);
3. BadRequest(ModelStateDictionary modelState);
Even with #3, a model state dictionary is ultimate a deep collection with one layer upon another, at the bottom of which, though, is a bunch of KeyValuePair<string, ModelError> where each ModelError also only has either a string or an Exception object.
Therefore, even with #3, we are only able to pack a string to send and not a custom object like I want to.
I am really not asking how I may go about working a hack or a kludge around the situation. My question is: is there an overload or another way baked into the .NET API to send an object to the client with a Bad Request HTTP status code?
I am using ASP.NET Web API version 5.2.4 targeting .NET Framework version 4.6.1.
You can use the Content<T>(...) method to do this. It returns a NegotiatedContentResult, which is serialized depending on the request headers (e.g. json, xml), and allows you to specify a HttpStatusCode.
You can use it like this:
return Content(HttpStatusCode.BadRequest, myObject);
If you wanted to, you could create your own BadRequest<T>(T obj) method in the controller as a wrapper, so then you could call it as you wanted:
public IHttpActionResult BadRequest<T>(T obj)
{
return Content(HttpStatusCode.BadRequest, obj);
}
public IHttpActionResult Action()
{
// do whatever validation here.
var validationResult = Validate();
// then return a bad request
return BadRequest(validationResult);
}
You can build/format the string in JSON format, pass it as string in the BadRequest() parameter and convert it to JSON again or any object on the caller's backend.
Haven't tried that but that should work.

Add Authorization header to all requests in Xamarin Forms Android WebView

I'm trying to add custom http headers to a webview client (for authorization).
It seems to work in some cases, I'am able to login to a webpage without entering username and password, and I get redirected to another page. But when the page is calling other resources to get elements populated with data an error is thrown and OnReceivedHttpError is invoked. The error I'm getting is 401 unauthorized and when i look through the headers on the IWebResourceRequest i can't see the authorization headers at all.
Am I missing something or have anyone had same problems ?
Using Xamarin Forms 2.3.3.180 and targeting API 21 (Android 5.0 Lollipop), compile with Android 7.1 Nougat.
I've tried in postman to add headers to request and it works perfectly.
Renderer:
public class MyWebViewRenderer : WebViewRenderer
{
private MyWebViewClient _webViewClient;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if(_webViewClient == null)
_webViewClient = new MyWebViewClient();
Control.SetWebViewClient(_webViewClient);
Control.LongClickable = false;
Control.HapticFeedbackEnabled = false;
Control.Settings.JavaScriptEnabled = true;
var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);
var headers = new Dictionary<string, string>();
headers.Add("Authorization", $"Basic {base64string}")
Control.LoadUrl(Control.Url, headers);
}
}
WebViewClient:
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
WebView.SetWebContentsDebuggingEnabled(true);
var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);
var headers = new Dictionary<string, string>();
headers.Add("Authorization", $"Basic {base64string}")
view.LoadUrl(url, headers);
return true;
}
public override WebResourceResponse ShouldInterceptRequest(WebView view, IWebResourceRequest urlResource)
{
//headers does not always contains authorization header, so let's add it.
if (!urlResource.RequestHeaders.ContainsKey("authorization") && !urlResource.RequestHeaders.ContainsKey("Authorization"))
{
var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);
urlResource.RequestHeaders.Add("Authorization", $"{base64string}");
}
return base.ShouldInterceptRequest(view, urlResource);
}
public override void OnReceivedHttpError(WebView view, IWebResourceRequest request, WebResourceResponse errorResponse)
{
base.OnReceivedHttpError(view, request, errorResponse);
}
If you only need the headers on the get requests, the code below will work. However POST requests are a different issue. I needed to do a similar thing (with all requests, not just GET), and all I can say is that there's not straightforward solution, at least not one that I've found (and I've tried everything short of writing my own network driver). I've tried so many methods (ShouldOverrideUrlLoading, ShouldInterceptRequest, custom LoadUrl and PostUrl etc.) and none of them give a 100% solution. There is a lot of misinformation about this so I think some clarification is needed since I've spent two days on this without success.
So here's what I've learned:
If you only need the headers in the GET requests, that's trivial. Simply create an implementation of WebViewClient and override ShouldOverrideUrlLoading like this:
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(App.Android.HybridWebViewRenderer))]
namespace App.Android
{
public class HybridWebViewRenderer : WebViewRenderer
{
public HybridWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
Control.SetWebViewClient(new CustomWebViewClient());
}
}
public class CustomWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
{
Dictionary<string, string> headers = new Dictionary<string, string>
{
["Name"] = "value"
};
view.LoadUrl(url, headers);
return true;
}
}
}
If, however, you need the headers in other requests (specifically POST requests) there really isn't a perfect solution. Many answers tell you to override ShouldInterceptRequest but this is unlikely to help. ShouldInterceptRequest provides an IWebResourceRequest which contains the URL of the request, the method (i.e. POST) and the headers. There are answers out there which state that adding the headers by doing request.Headers.Add("Name", "Value") is a viable solution but this is wrong. The IWebResourceRequest is not used by the WebView's internal logic so modifying it is useless!
You can write your own HTTP client in ShouldInterceptRequest which includes your own headers to perform the requests and return a WebResourceResponse object. Again, this works for GET requests, but the problem with this is that even though we can intercept a POST request, we cannot determine the content in the request as the request content is not included in the IWebResourceRequest object. As a result, we cannot accurately perform the request manually. So, unless the content of the POST request is unimportant or can somehow be fetched, this method is not viable.
An additional note on this method: returning null tells the WebView to handle the request for us. In other words 'I don't want to intercept the request'. If the return is not null however, the WebView will display whatever is in the WebResourceResponse object.
I also tried overriding the PostUrl and LoadUrl methods in the WebView itself. These methods are not called by the internal logic, so unless you are calling them yourself, this does not work.
So what can be done? There are a few hacky solutions (see github.com/KeejOow/android-post-webview) to get around this problem, but they rely on javascript and are not suitable in all cases (I have read that they don't work with forms). If you want to use them in Xamarin, you're going to need to adapt the code for C# anyway, and there is no guarantee that it will solve your problem.
I'm writing this so no one else has to waste countless hours finding a solution that doesn't really exist.
If only the Android devs had decided to include the POST content in the IWebResourceRequest object...
And apologies for the length, if you've read to this point, you're probably as desperate as I was.

What kind of datatypes should I use for the return value of a Web API method?

I have a Web API controller that returns data to my client. The code looks like this:
[HttpGet]
[ActionName("Retrieve")]
public IEnumerable<Reference> Retrieve(int subjectId)
{
return _referenceService.Retrieve(subjectId);
}
Can someone tell me is it necessary to specify the ActionName?
Also should I return an IEnumerable, an IList or something else?
I believe if your ASP.NET routing is setup correctly you don't need to specify the ActionName, for example:
protected void Application_Start()
{
RouteTable.Routes.MapHttpRoute("0", "{controller}/{action}/{arg1}");
}
Will match /YourControllerName/Retrieve/132
What you return is based entirely on your media-type formatters, of which the default is XmlFormatter and JsonFormatter. These can be found in GlobalConfiguration.Configuration.Formatters and will be chosen based on the Accept header provided by the client.
We, for example, use JSON.Net for our response formatting, configured by:
protected void Application_Start()
{
RouteTable.Routes.MapHttpRoute("0", "{controller}/{action}/{arg1}");
MediaTypeFormatterCollection formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
jsonFormatter.Formatting = Formatting.Indented;
jsonFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
This tells WebApi to disallow any XML formatting and only return JSON using the provided JSON.Net contract resolver. JSON.Net supports serializing IEnumerable.
I would, however, recommend returning a HttpResponseMessage instead. This allows you to set the status code as well (This still uses the media type formatter, it's just a cleaner wrapper). You can use this like so:
[HttpGet]
public HttpResponseMessage Retrieve(int subjectId)
{
var response _referenceService.Retrieve(subjectId);
return Request.CreateResponse(HttpStatusCode.OK, response);
}
You should return HttpStatusCode instead of data if have not requirement, like POST method should return OK or whatever.
or if want record like Get method should return type of record.
also you no need to add attribute on method like Get,Put,Delete etc because webapi automatically detect method according to action like if you are getting data then your method name should be start with Get like GetEmployee etc.

Controlling serialization in Web API / APIController

Where can I specify custom serialization/deserialization in an ASP.NET Web API?
The throughput of our application requires a fast serialization/deserialization of messages, hence we need to tightly control this part of the code to either use our home-brew or an OSS one out there.
I have checked various sources such as this that explains how to create a custom value provider, but I have yet to see an example that explains the process end to end.
Can anyone direct/show me the way to serialize the incoming/outgoing messages?
Also a diagram of the various injection points/event sinks in Web API similar to this one for WCF is appreciated!
The extension point you're looking for is the MediaTypeFormatter. It controls reading from the request body and writing to the response body. This might be the best resource for writing your own formatter:
http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters
Here's code example in case link in the answer above dies
public class MerlinStringMediaTypeFormatter : MediaTypeFormatter
{
public MerlinStringMediaTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
public override bool CanReadType(Type type)
{
return type == typeof (YourObject); //can it deserialize
}
public override bool CanWriteType(Type type)
{
return type == typeof (YourObject); //can it serialize
}
public override Task<object> ReadFromStreamAsync(
Type type,
Stream readStream,
HttpContent content,
IFormatterLogger formatterLogger)
{
//Here you put deserialization mechanism
return Task<object>.Factory.StartNew(() => content.ReadAsStringAsync().Result);
}
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
//Here you would put serialization mechanism
return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
}
}
Then you need to register your formatter in Global.asax
protected void Application_Start()
{
config.Formatters.Add(new MerlinStringMediaTypeFormatter());
}
Hope this saves you some time.

Resources