ContentObserver with URI doesnt work for Contacts in API Level 16 - uri

I am trying following code:
private class NativeContentObserver extends ContentObserver {
public NativeContentObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Log.i(TAG, "uri is "+uri);
}
{
NativeContentObserver contentObserver = new NativeContentObserver();
mContext.getContentResolver().registerContentObserver(
ContactsContract.Contacts.CONTENT_URI, true, contentObserver);
}
When I run this code and edit a contact, I see
uri is content://com.android.contacts
WHY I am not getting the ID of individual contact in URI? If I try SMS observer, I do get correct URI which contains ID there.
Anything I am missing here? This is on Samsung S3 handset.

most probably because provideres update method calls notifyChange(uri) with complete URI, instead of filtered one. if this is the case, that means ContactsContract.Contacts has been deprecated. search for alternatives

Related

Serve static content in Spring Boot despite using #RequestMapping("**")

The context
I am currently working on an educational project. This implies two Spring Boot REST servers. One is an actual server, which does some processing.
The one I'm interested in is the other. It is a proxy which will redirect all calls to the first one. So that when I call http://localhost:8080/foo, my proxy server will in turn call http://localhost:8090/foo. And if the first server returns A, the proxy will return {"proxied": A, "someInformationAboutThisCall": B}.
I managed to get to this point with some probably inelegant but functioning code of which I give an excerpt below. The key here is that I use #RequestMapping("**") to achieve this. The next step is to design an interface that will make my additional information immediately legible, which is basically the point of this project. If I remove all #RequestMapping("**"), it works just fine.
The question
Now my problem is the following: having used #RequestMapping("**"), I cannot serve static content (the calls get redirect to the other REST server, which does not serve static content). How could I configure Spring Boot/Spring MVC to ignore resources available as static content when mapping the requests, or make the PathResourceResolver prioritary over my controller?` Or should I serve my static content from yet another JVM/server?
Thanks in advance for your help!
Edit of interest: while doing some tests, I discovered that the static content is served, with some restrictions, if I use #RequestMapping("*").
/index.html generates an error page (as does more generally any static content directly in public)
/itf/index.html works (as does more generally any file in public/itf or any other subdirectory of public)
/itf does not work: Spring Boot seems unaware of an index file in it. I must specify a full URI, down to the specific file I want to display.
This however does not work at all with #RequestMapping("**"), which I need.
The tentatives
I tried using a WebMvcConfigurerAdapter with an HandlerInterceptorAdapter (found on SO, SO again and many other places on the Internet), but could not start my project anymore because Spring boot then does not find the InterceptorRegistry bean (has there been recent changes in Spring Boot? I'm using the version 1.5.3.RELEASE).
I also tried some anti-matching but not only does it not work, it also feels very very dirty (and this whole project is probably not optimal, so that's saying a lot).
The code samples for the curious
My "proxy" controller
Note: you can suggest better ways to realize this in comments. Please keep in mind that, though I'm always open to enhancement suggestions, this was not my question.
#RestController
public class ProxyController {
#Value("${monitored.url.base}") // "http://localhost:8090"
private String redirectBase;
#RequestMapping(value = "**", method = {RequestMethod.POST, RequestMethod.PUT})
public ProxiedResponse proxifyRequestsWithBody(HttpServletRequest request, #RequestHeader HttpHeaders headers, #RequestBody Object body) throws URISyntaxException {
return proxifyRequest(request, headers, body);
}
#RequestMapping(value = "**")
public ProxiedResponse proxifyRequestsWithoutBody(HttpServletRequest request, #RequestHeader HttpHeaders headers) throws URISyntaxException {
return proxifyRequest(request, headers, null);
}
private ProxiedResponse proxifyRequest(HttpServletRequest request, #RequestHeader HttpHeaders headers, #RequestBody Object body) throws URISyntaxException {
final RequestEntity<Object> requestEntity = convertToRequestEntity(request, headers, body);
// call remote service
final ResponseEntity<Object> proxied = restTemplate.exchange(requestEntity, Object.class);
// Return service result + monitoring information
final ProxiedResponse response = new ProxiedResponse();
response.setProxied(proxied.getBody());
// set additional information
return response;
}
// Won't work properly for POST yet
private <T> RequestEntity<T> convertToRequestEntity(HttpServletRequest request, HttpHeaders headers, T body) throws URISyntaxException {
// Build proxied URL
final StringBuilder redirectUrl = new StringBuilder(redirectBase).append(request.getRequestURI());
final String queryString = request.getQueryString();
if (queryString != null) {
redirectUrl.append("?").append(queryString);
}
// TODO enhancement: transmit headers and request body to make this a real proxy
final HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod());
return new RequestEntity<>(body, headers, httpMethod, new URI(redirectUrl.toString()));
}
}
My dirty attempt at excluding static resources URLs
#Configuration // adding #EnableWebMvc did not solve the problem
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
private static class StaticResourcesHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String requestURI = request.getRequestURI();
if (requestURI == null || "/".equals(requestURI) || "/index.html".equals(requestURI) || requestURI.startsWith("/assets")) {
return super.preHandle(request, response, null);
}
return super.preHandle(request, response, handler);
}
}
#Autowired
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new StaticResourcesHandlerInterceptor()).addPathPatterns("/**");
}
}
You can split the path into a wild-card, and a named path variable which must match a negative lookahead regular expression.
#RequestMapping("/{variable:(?!static).*}/**")
You can then use #PathVariable String variable as an argument of your controller method to obtain the value of variable if you need to pass it.
(Would rather have written a comment but I have insufficient reputation)
Try to add the #EnableWebMvc annotation to your configuration:
#Configuration
#EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
...
}

HttpMediaTypeNotAcceptableException: Could not find acceptable representation. When with special url

#RequestMapping(value = "/servers/{domain}", method = RequestMethod.GET)
public Server getMailServer(#PathVariable("domain") String domain)
Server server = null;
try {
server = getServerByDomain(domain);
}
catch(Exception e){
}
return server;
}
When I call "http://localhost:8080/server/hotmail.com" with HttpClient Get method,the value of variable domain is "hotmail", not "hotmail.com".And I got the error:
HttpMediaTypeNotAcceptableException: Could not find acceptable representation.
But if I call "http://localhost:8080/server/hotmail", it works well.
I hope someone can see what is causing this issue.
This might be the same issue I had (and also this guy: https://stick2code.blogspot.co.at/2014/03/solved-orgspringframeworkwebhttpmediaty.html)
My service offers operations on files, i.e., /files/check/foo.txt
I always got a HttpMediaTypeNotAcceptableException and my rest handler was never actually called.
The problem is, that Spring has a feature where it tries to detect the requested content type by the path extension. So .com could mean a COM File. (See http://docs.spring.io/spring/docs/4.3.3.RELEASE/spring-framework-reference/htmlsingle/#mvc-config-content-negotiation and http://docs.spring.io/spring/docs/4.3.3.RELEASE/spring-framework-reference/htmlsingle/#mvc-config-path-matching)
My minimal #Config fix:
#Configuration
#EnableWebMvc
public class PathDispatchConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
configurer.setUseRegisteredSuffixPatternMatch(false);
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}
Change your #RequestMapping to following:)
#RequestMapping(value = "/servers/{domain:.+}", method = RequestMethod.GET)
I had the same problem because I had usernames (=mail adresses) in my request url. Thanks to the post of Benjamin Maurer I found the problem. However, I fixed it in a different way because I was unsure of the consequences regarding security of his answer.
I added the usernames as request parameters in the request. In your case, this would look something like:
#RequestMapping(value = "/servers", method = RequestMethod.GET)
public Server getMailServer(#RequestParam("domain") String domain) {
// do stuff
}
The request url would then look something like:
http://localhost:8080/server?domain=hotmail.com

Override host of webapi odata links

I'm using WebAPI 2.2 and Microsoft.AspNet.OData 5.7.0 to create an OData service that supports paging.
When hosted in the production environment, the WebAPI lives on a server that is not exposed externally, hence the various links returned in the OData response such as the #odata.context and #odata.nextLink point to the internal IP address e.g. http://192.168.X.X/<AccountName>/api/... etc.
I've been able to modify the Request.ODataProperties().NextLink by implementing some logic in each and every ODataController method to replace the internal URL with an external URL like https://account-name.domain.com/api/..., but this is very inconvenient and it only fixes the NextLinks.
Is there some way to set an external host name at configuration time of the OData service? I've seen a property Request.ODataProperties().Path and wonder if it's possible to set a base path at the config.MapODataServiceRoute("odata", "odata", GetModel()); call, or in the GetModel() implementation using for instance the ODataConventionModelBuilder?
UPDATE: The best solution I've come up with so far, is to create a BaseODataController that overrides the Initialize method and checks whether the Request.RequestUri.Host.StartsWith("beginning-of-known-internal-IP-address") and then do a RequestUri rewrite like so:
var externalAddress = ConfigClient.Get().ExternalAddress; // e.g. https://account-name.domain.com
var account = ConfigClient.Get().Id; // e.g. AccountName
var uriToReplace = new Uri(new Uri("http://" + Request.RequestUri.Host), account);
string originalUri = Request.RequestUri.AbsoluteUri;
Request.RequestUri = new Uri(Request.RequestUri.AbsoluteUri.Replace(uriToReplace.AbsoluteUri, externalAddress));
string newUri = Request.RequestUri.AbsoluteUri;
this.GetLogger().Info($"Request URI was rewritten from {originalUri} to {newUri}");
This perfectly fixes the #odata.nextLink URLs for all controllers, but for some reason the #odata.context URLs still get the AccountName part (e.g. https://account-name.domain.com/AccountName/api/odata/$metadata#ControllerName) so they still don't work.
Rewriting the RequestUri is sufficient to affect #odata.nextLink values because the code that computes the next link depends on the RequestUri directly. The other #odata.xxx links are computed via a UrlHelper, which is somehow referencing the path from the original request URI. (Hence the AccountName you see in your #odata.context link. I've seen this behavior in my code, but I haven't been able to track down the source of the cached URI path.)
Rather than rewrite the RequestUri, we can solve the problem by creating a CustomUrlHelper class to rewrite OData links on the fly. The new GetNextPageLink method will handle #odata.nextLink rewrites, and the Link method override will handle all other rewrites.
public class CustomUrlHelper : System.Web.Http.Routing.UrlHelper
{
public CustomUrlHelper(HttpRequestMessage request) : base(request)
{ }
// Change these strings to suit your specific needs.
private static readonly string ODataRouteName = "ODataRoute"; // Must be the same as used in api config
private static readonly string TargetPrefix = "http://localhost:8080/somePathPrefix";
private static readonly int TargetPrefixLength = TargetPrefix.Length;
private static readonly string ReplacementPrefix = "http://www.contoso.com"; // Do not end with slash
// Helper method.
protected string ReplaceTargetPrefix(string link)
{
if (link.StartsWith(TargetPrefix))
{
if (link.Length == TargetPrefixLength)
{
link = ReplacementPrefix;
}
else if (link[TargetPrefixLength] == '/')
{
link = ReplacementPrefix + link.Substring(TargetPrefixLength);
}
}
return link;
}
public override string Link(string routeName, IDictionary<string, object> routeValues)
{
var link = base.Link(routeName, routeValues);
if (routeName == ODataRouteName)
{
link = this.ReplaceTargetPrefix(link);
}
return link;
}
public Uri GetNextPageLink(int pageSize)
{
return new Uri(this.ReplaceTargetPrefix(this.Request.GetNextPageLink(pageSize).ToString()));
}
}
Wire-up the CustomUrlHelper in the Initialize method of a base controller class.
public abstract class BaseODataController : ODataController
{
protected abstract int DefaultPageSize { get; }
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var helper = new CustomUrlHelper(controllerContext.Request);
controllerContext.RequestContext.Url = helper;
controllerContext.Request.ODataProperties().NextLink = helper.GetNextPageLink(this.DefaultPageSize);
}
Note in the above that the page size will be the same for all actions in a given controller class. You can work around this limitation by moving the assignment of ODataProperties().NextLink to the body of a specific action method as follows:
var helper = this.RequestContext.Url as CustomUrlHelper;
this.Request.ODataProperties().NextLink = helper.GetNextPageLink(otherPageSize);
The answer by lencharest is promising, but I found an improvement on his method. Rather than using the UrlHelper, I created a class derived from System.Net.Http.DelegatingHandler. This class is inserted (first) into the message handling pipeline and thus has a crack at altering the incoming HttpRequestMessage. It's an improvement over the above solution because in addition to altering the controller-specific URLs (as the UrlHelper does, e,g, https://data.contoso.com/odata/MyController), it also alters the url that appears as the xml:base in the OData service document (e.g., https://data.contoso.com/odata).
My particular application was to host an OData service behind a proxy server, and I wanted all the URLs presented by the server to be the externally-visible URLs, not the internally-visible ones. And, I didn't want to have to rely on annotations for this; I wanted it to be fully automatic.
The message handler looks like this:
public class BehindProxyMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var builder = new UriBuilder(request.RequestUri);
var visibleHost = builder.Host;
var visibleScheme = builder.Scheme;
var visiblePort = builder.Port;
if (request.Headers.Contains("X-Forwarded-Host"))
{
string[] forwardedHosts = request.Headers.GetValues("X-Forwarded-Host").First().Split(new char[] { ',' });
visibleHost = forwardedHosts[0].Trim();
}
if (request.Headers.Contains("X-Forwarded-Proto"))
{
visibleScheme = request.Headers.GetValues("X-Forwarded-Proto").First();
}
if (request.Headers.Contains("X-Forwarded-Port"))
{
try
{
visiblePort = int.Parse(request.Headers.GetValues("X-Forwarded-Port").First());
}
catch (Exception)
{ }
}
builder.Host = visibleHost;
builder.Scheme = visibleScheme;
builder.Port = visiblePort;
request.RequestUri = builder.Uri;
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
You wire the handler up in WebApiConfig.cs:
config.Routes.MapODataServiceRoute(
routeName: "odata",
routePrefix: "odata",
model: builder.GetEdmModel(),
pathHandler: new DefaultODataPathHandler(),
routingConventions: ODataRoutingConventions.CreateDefault()
);
config.MessageHandlers.Insert(0, new BehindProxyMessageHandler());
There is another solution, but it overrides url for the entire context.
What I'd like to suggest is:
Create owin middleware and override Host and Scheme properties inside
Register the middleware as the first one
Here is an example of middleware
public class RewriteUrlMiddleware : OwinMiddleware
{
public RewriteUrlMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
context.Request.Host = new HostString(Settings.Default.ProxyHost);
context.Request.Scheme = Settings.Default.ProxyScheme;
await Next.Invoke(context);
}
}
ProxyHost is the host you want to have. Example: test.com
ProxyScheme is the scheme you want: Example: https
Example of middleware registration
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(typeof(RewriteUrlMiddleware));
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
A couple of years later, using ASP.NET Core, I figured that the easiest way to apply it in my service was to just create a filter that masquerades the host name. (AppConfig is a custom configuration class that contains the host name, among other things.)
public class MasqueradeHostFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var appConfig = context.HttpContext.RequestServices.GetService<AppConfig>();
if (!string.IsNullOrEmpty(appConfig?.MasqueradeHost))
context.HttpContext.Request.Host = new HostString(appConfig.MasqueradeHost);
}
}
Apply the filter to the controller base class.
[MasqueradeHostFilter]
public class AppODataController : ODataController
{
}
The result is a nicely formatted output:
{ "#odata.context":"https://app.example.com/odata/$metadata" }
Just my two cents.
Using system.web.odata 6.0.0.0.
Setting the NextLink property too soon is problematic. Every reply will then have a nextLink in it. The last page should of course be free of such decorations.
http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793048 says:
URLs present in a payload (whether request or response) MAY be
represented as relative URLs.
One way that I hope will work is to override EnableQueryAttribute:
public class myEnableQueryAttribute : EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
var result = base.ApplyQuery(queryable, queryOptions);
var nextlink = queryOptions.Request.ODataProperties().NextLink;
if (nextlink != null)
queryOptions.Request.ODataProperties().NextLink = queryOptions.Request.RequestUri.MakeRelativeUri(nextlink);
return result;
}
}
ApplyQuery() is where the "overflow" is detected. It basically asks for pagesize+1 rows and will set NextLink if the result set contains more than pagesize rows.
At this point it is relatively easy to rewrite NextLink to a relative URL.
The downside is that every odata method must now be adorned with the new myEnableQuery attribute:
[myEnableQuery]
public async Task<IHttpActionResult> Get(ODataQueryOptions<TElement> options)
{
...
}
and other URLs embedded elsewhere remains problematic. odata.context remains a problem. I want to avoid playing with the request URL, because I fail to see how that is maintainable over time.
Your question boils down to controlling the service root URI from within the service itself. My first thought was to look for a hook on the media type formatters used to serialize responses. ODataMediaTypeFormatter.MessageWriterSettings.PayloadBaseUri and ODataMediaTypeFormatter.MessageWriterSettings.ODataUri.ServiceRoot are both settable properties that suggest a solution. Unfortunately, ODataMediaTypeFormatter resets these properties on every call to WriteToStreamAsync.
The work-around is not obvious, but if you dig through the source code you'll eventually reach a call to IODataPathHandler.Link. A path handler is an OData extension point, so you can create a custom path handler that always returns an absolute URI which begins with the service root you desire.
public class CustomPathHandler : DefaultODataPathHandler
{
private const string ServiceRoot = "http://example.com/";
public override string Link(ODataPath path)
{
return ServiceRoot + base.Link(path);
}
}
And then register that path handler during service configuration.
// config is an instance of HttpConfiguration
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel(),
pathHandler: new CustomPathHandler(),
routingConventions: ODataRoutingConventions.CreateDefault()
);

Capture ALL WebAPI requests

I would like to capture and save in a log file all the requests that my WebAPI should handle.
Just tried to save the Request.Content from the controller constructor but unfortunately,
the request object is null from the controller constructor scope.
Hope to learn an efficient way to do it.
I would just hook into web api tracing...
http://www.asp.net/web-api/overview/testing-and-debugging/tracing-in-aspnet-web-api
From the above article, you can implement ITraceWriter like so. This example uses System.Diagnostics.Trace.WriteLine, but you could plug in writing to a file here as well.
public class SimpleTracer : ITraceWriter
{
public void Trace(HttpRequestMessage request, string category, TraceLevel level,
Action<TraceRecord> traceAction)
{
TraceRecord rec = new TraceRecord(request, category, level);
traceAction(rec);
WriteTrace(rec);
}
protected void WriteTrace(TraceRecord rec)
{
var message = string.Format("{0};{1};{2}",
rec.Operator, rec.Operation, rec.Message);
System.Diagnostics.Trace.WriteLine(message, rec.Category);
}
}
As you can see from the Trace method, you get access to the HttpRequestMessage here.
I ended up implementing middleware to deal with it.
public class GlobalRequestLogger : OwinMiddleware
{
public override Task Invoke(IOwinContext context)
{
// Implement logging code here
}
}
Then in your Startup.cs:
app.Use<GlobalRequestLogger>();

How can I MapHttpRoute a POST to a custom action using the WebApi?

I'm trying to figure out the madness behind the Web API routing.
When I try to post data like this:
curl -v -d "test" http://localhost:8088/services/SendData
I get a 404, and the following error message:
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:8088/services/SendData'.","MessageDetail":"No action was found on the controller 'Test' that matches the request."}
Here is the code for my test server.
public class TestController : ApiController
{
[HttpPost]
public void SendData(string data)
{
Console.WriteLine(data);
}
}
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8088");
config.Routes.MapHttpRoute(
name: "API Default",
routeTemplate:"services/SendData",
defaults: new { controller = "Test", action = "SendData"},
constraints: null);
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
More generally, why has the ASP.NET team decided to make the MapHttpRoute method so confusing. Why does it take two anonymous objects....how is anyone supposed to know what properties these objects actually need?
MSDN gives no help: http://msdn.microsoft.com/en-us/library/hh835483(v=vs.108).aspx
All the pain of a dynamically typed language without any of the benefit if you ask me...
Agree with you, it's a hell of a madness, you need to specify that the data parameter should be bound from the POST payload, since the Web API automatically assumes that it should be part of the query string (because it is a simple type):
public void SendData([FromBody] string data)
And to make the madness even worse you need to prepend the POST payload with = (yeah, that's not a typo, it's the equal sign):
curl -v -d "=test" http://localhost:8088/services/SendData
You could read more about the madness in this article.
Or stop the madness and try ServiceStack.
Use this signature and it will work every time.
public class TestController : ApiController
{
[HttpPost]
[ActionName("SendData")]
public HttpResponseMessage SendData(HttpRequestMessage request)
{
var data = request.Content.ReadAsStringAsync().Result;
Console.WriteLine(data);
}
}
Try with the following change,
public class TestController : ApiController
{
[HttpPost]
[ActionName("SendData")]
public void SendData(string data)
{
Console.WriteLine(data);
}
}
The ActionName attribute might fix the issue. Otherwise, you can also the name convention "Post"
public void Post(string data)
{
Console.WriteLine(data);
}
And send an Http Post directly to "services" without SendData.

Resources