I have an Odata Service and a WPF client application.
Some of the Odata Service Entities have images attached to them (ie.Client).
The streaming works as long as I do not apply authentication. I can view and change the images. Once I enforce authentication everything works as expected, given the credentials check out. All but the images that is. Here are the relevant code steps / snipes.
Window Constructor code
bool iv = System.Web.Security.Membership.ValidateUser("userName", "pass");
ManageService = new InventoryContext(new Uri(...));
ManageService.SendingRequest += new EventHandler<SendingRequestEventArgs (ManageService_SendingRequest);
ManageService_SendingRequest code
//attach the authentication cookie to the request header
((HttpWebRequest)e.Request).CookieContainer = ((ClientFormsIdentity)Thread.CurrentPrincipal.Identity).AuthenticationCookies;
The call to fetch the data is async using background worker
Query Methode()
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(FetchClient);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FetchClientsCompleted);
worker.RunWorkerAsync(ClientUUID);
FetchClient
var query = from o in ManageService.Clients where o.ClientUUID.Equals((Guid)e.Argument)
...
e.Result = query;
FetchClientsCompleted
var res = e.Result as DataServiceCollection<Client>;
DataContext = res[0]; //this is all working, with and without authentication
//the next line, binding the stream to the image throws 'unauthenticated'
//it works well if authentication is disabled
imgClient.Source = new BitmapImage(ManageService.GetReadStreamUri(DataContext));
if I debug, the SendingRequest methode, usually called with any query request is NOT triggered calling GetReadStreamUri(...).
This is where I am stuck, what to do to authenticate to the service to get the stream?
Also, I took the URI generated by ManageService.GetReadStreamUri(DataContext), past it into the browser and it works, the image is displayed in the browser, if logged in.
Anyone any ideas?
The SendingRequest handler will only fire for request sent by the DataServiceContext class (your ManageService). But in the case of the picture, you only get the URL from the DataServiceContext and then let the BitmapImage actually issue the HTTP request to that URL. So the event won't fire for that request. I don't know if BitmapImage has a way for you to hook into the HTTP request pipeline (I don't think it does).
You could issue that request yourself and then use the response stream as the input for the bitmap image, in which case you get full control over the request and thus can implement authentication as appropriate.
Related
I am using WPF control for CefSharp. I need to know when the request I made receives a response with http status code 404.
I've noticed that CefSharp has LoadError event, but that only fires when the domain cannot be resolved altogether (i.e. if I go to www.sdfhjkhajsdf.com). It doesn't work for when the domain exists, but the page your requesting doesn't.
This is a pretty old question. CefSharp has had lots of great updates, so I hope this helps others searching like me. Don't hate me because my snippets are in VB.NET. ;)
This is what I'm doing to log anything that is not 200 response. For my application, I open a new form of the requested page, so I limit my focus to the initial page request by looking at the ReferrerUrl property. Obviously, you could drop that part to be alerted to all requests.
When you initially implement your ChromiumWebBrowser you need to set a RequestHandler to your implementation.
Me.chromeBrowser = New CefSharp.WinForms.ChromiumWebBrowser(uri)
Me.chromeBrowser.RequestHandler = New CefBasicRequestHandler()
Me.Controls.Add(Me.chromeBrowser)
CefSharp released a default implementation that you can use and just override OnResourceResponse.
Imports System.Security.Cryptography.X509Certificates
Imports CefSharp
Public Class CefBasicRequestHandler
Inherits CefSharp.Handler.DefaultRequestHandler
Private Shared ReadOnly Logger As log4net.ILog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
Public Overrides Function OnResourceResponse(browserControl As IWebBrowser, browser As IBrowser, frame As IFrame, request As IRequest, response As IResponse) As Boolean
If String.IsNullOrWhiteSpace(request.ReferrerUrl) Then ' this is the first request of the page
Dim method As String = "OnResourceResponse()."
Dim requestOverview As String = $"[{response.StatusCode}] [{request.Url}]"
Logger.Info($"{method} {requestOverview}")
If response.StatusCode <> 200 Then
Logger.Warn($"{method} {requestOverview}")
End If
End If
Return MyBase.OnResourceResponse(browserControl, browser, frame, request, response)
End Function
End Class
tl;dr What is the best way to pass binary data (up to 1MBish) from a WPF application to a WebAPI service method?
I'm currently trying to pass binary data from a WPF application to a WebAPI web service, with variable results. Small files (< 100k) generally work fine, but any larger and the odds of success reduce.
A standard OpenFileDialog, and then File.ReadAllBytes pass the byte[] parameter into the client method in WPF. This always succeeds, and I then post the data to WebAPI via a PostAsync call and a ByteArrayContent parameter.
Is this the correct way to do this? I started off with a PostJSONAsync call, and passed the byte[] into that, but thought the ByteArrayContent seemed more appropriate, but neither work reliably.
Client Method in WPF
public static async Task<bool> UploadFirmwareMCU(int productTestId, byte[] mcuFirmware)
{
string url = string.Format("productTest/{0}/mcuFirmware", productTestId);
ByteArrayContent bytesContent = new ByteArrayContent(mcuFirmware);
HttpResponseMessage response = await GetClient().PostAsync(url, bytesContent);
....
}
WebAPI Method
[HttpPost]
[Route("api/productTest/{productTestId}/mcuFirmware")]
public async Task<bool> UploadMcuFirmware(int productTestId)
{
bool result = false;
try
{
Byte[] mcuFirmwareBytes = await Request.Content.ReadAsByteArrayAsync();
....
}
Web Config Settings
AFAIK these limits in web.config should be sufficient to allow 1MB files through to the service?
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
<httpRuntime targetFramework="4.5" maxRequestLength="2097152"/>
I receive errors in WebAPI when calling ReadAsByteArrayAsync(). These vary, possibly due to the app pool in IIS Express having crashed / getting into a bad state, but they include the following (None of which have lead to any promising leads via google):
Specified argument was out of the range of valid values. Parameter name: offset
at System.Web.HttpInputStream.Seek(Int64 offset, SeekOrigin origin)\r\n
at System.Web.HttpInputStream.set_Position(Int64 value)\r\n at System.Web.Http.WebHost.SeekableBufferedRequestStream.SwapToSeekableStream()\r\n at System.Web.Http.WebHost.Seek
OR
Message = "An error occurred while communicating with the remote host. The error code is 0x800703E5."
InnerException = {"Overlapped I/O operation is in progress. (Exception from HRESULT: 0x800703E5)"}
at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)\r\n
at System.Web.Hosting.IIS7WorkerRequest.ReadEntityCoreSync(Byte[] buffer, Int32 offset, Int32 size)\r\n
at System.Web.Hosting.IIS7WorkerRequ...
Initially I thought this was most likely down to IIS Express limitations (running on Windows 7 on my dev pc) but we've had the same issues on a staging server running Server 2012.
Any advice on how I might get this working would be great, or even just a basic example of uploading files to WebAPI from WPF would be great, as most of the code I've found out there relates to uploading files from multipart forms web pages.
Many thanks in advance for any help.
tl;dr It was a separate part of our code in the WebApi service that was causing it to go wrong, duh!
Ah, well, this is embarrassing.
It turns out our problem was down to a Request Logger class we'd registered in WebApiConfig.Register(HttpConfiguration config), and that I'd forgotten about.
It was reading the request content via async as StringContent, and then attempting to log it to the database in an ncarchar(max) field. This itself is probably OK, but I'm guessing all the weird problems started occurring when the LoggingHandler as well as the main WebApi controller, were both trying to access the Request content via async?
Removing the LoggingHandler fixed the problem immediately, and we're now able to upload files of up to 100MB without any problems. To fix it more permanently, I guess I rewrite of the LoggingHandler is required to set a limit on the maximum content size it tries to log / to ignore certain content types.
It's doubtful, but I hope this may be of use for someone one day!
public class LoggingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LogRequest(request);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
// ToDo: Decide if/when we need to log responses
// LogResponse(response);
return response;
}, cancellationToken);
}
private void LogRequest(HttpRequestMessage request)
{
(request.Content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
try
{
var callerId = CallerId(request);
var callerName = CallerName(request);
// Log request
LogEntry logEntry = new LogEntry
{
TimeStamp = DateTime.Now,
HttpVerb = request.Method.ToString(),
Uri = request.RequestUri.ToString(),
CorrelationId = request.GetCorrelationId(),
CallerId = callerId,
CallerName = callerName,
Controller = ControllerName(request),
Header = request.Headers.ToString(),
Body = x.Result
};
...........
I was wondering how I can achieve something like an HTTPClient.
I tried WebBrowser class but it seems that the execution continues even though the URL specified has not yet loaded.
public void testWebBrowser(){
final WebBrowser b = new WebBrowser(){
#Override
public void onLoad(String url) {
BrowserComponent c = (BrowserComponent)this.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
// I want this Javascript context here
}
};
// just a test URL
b.setURL("http://youtube.com");
// Suppose to get the Javascript context here though it executes without waiting for the whole page to load
}
How can I get the JS Context from within a WebBrowser context? Like a synchronous execution
WebBrowser browser = new WebBrowser();
browser.setURL("someURL");
// wait execution till the whole page in "someURL" loads till it executes the next line
BrowserComponent c = (BrowserComponent)browser.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
If I understand correctly you are trying to create a scraping solution?
That's probably not the ideal approach since this will actually create a web browser which you then need to automate with JavaScript. I would suggest you create a webservice that encapsulates the HttpClient functionality and drive it with ConnectionRequest. This way when the web site changes you can just fix your server in a way seamless to your installed base.
I am having a problem making interceptors fire when using the WebClient class (org.apache.cxf.jaxrs.client.WebClient). In my method that calls a RESTful service I have added an interceptor to execute in the out phase. I have intentionally provided invalid properties so I can see the interceptor fail, but the method completes successfully.
Here is the code that I am working with:
private String callService2(String webServiceUrl) {
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(webServiceUrl);
// setup properties
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.signature.username", "client");
properties.put("ws-security.signature.properties",
"client_nonexistantfile.properties");
bean.setProperties(properties);
XmlSigOutInterceptor sigInterceptor = new XmlSigOutInterceptor();
bean.getOutInterceptors().add(sigInterceptor);
// use WebClient (or proxy) as usual
WebClient wc = bean.createWebClient();
TestInfoResponse response = wc.accept("application/xml").get(TestInfoResponse.class);
return response.getContents();
}
I am expecting the XmlSigOutInterceptor logic to fail because the properties file does not exist, but the method completes successfully. What am I doing wrong when adding the XmlSigOutInterceptor.
Thanks in advance.
This is my bad. the XmlSigOutInterceptor does not need to do anything when a get is performed because there is no document to sign. So the interceptor was firing, it just returned right away.
Sorry for the noise.
I am using GWT and Google App Engine Java for my application. I have a profile screen where
user enters profile information like name, age and address, saves it and gets success or failure message. I developed this initial application using GWT-RPC and it worked fine. I had a new requirement where I have to store image of the user. I am using BlobstoreService to store images. This has created complications in the flow. I had to use FormPanel as it is the only way to do a FileUpload in GWT. The BlobStore service servlet expects a redirect on completion. As a result it cannot now return any status back to my GWT application once the profile is saved. Is there easy to store images using GWT along with other form fields and show a status message back to user once the profile is saved.
i struggled a lot with this problem until yesterday I figured out the solution with much help from Ikai Lan's blog. Basicaly what I did is follow his steps but with a few modifications because doing it exactly how he did it did'nt work for me:
Create a form panel : set encoding multipart, method post.
Make a GWT Remote Service that just has one method:public String getUploadURL() or something like that and in the IMPL write this:
BlobstoreService service = BlobstoreServiceFactory.getBlobstoreService();
return service.createUploadUrl("/XXX/YYY");
In XXX you must put your project path, for example mine is com.fer.pyn.PictureYourNews
In YYY you must put the servlet mapping name for a new servlet that we will have to create: I put XXX = BlobUploader, I created a BlobUploader extends HttpServlet and you have to update the web.xml.
Okey, so this is the weird part that I could'nt figure out, thing is that when we make a RPC call to getUploadURL() in the remote ervice from step 2 that returns a weird addres, like: '/_ah/img/eq871HJL_bYxhWQbTeYYoA' and that is the .fromAction you have to put in your form from step one. You need to update the form's action every time so i suggest the following:
public void initBlobStoreSession()
{
imageService.getBlobStoreUploadURL(new AsyncCallback()
{
#Override
public void onSuccess(String result) {
uploadFormPanel.setAction(result);
System.out.println("Upload Form Panel Action set");
}
#Override
public void onFailure(Throwable caught) {
//oops
}
});
}
So when you submit your fromPanel, IT WILL UPLOAD THE BLOB and you dont have to do anything, the tricky part is how to get the blob:
What you need to do now is create the YYY servlet we where talking about in step 4.
In the post method, this is important:
private BlobstoreService blobService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, BlobKey> blobMap = blobService.getUploadedBlobs(request);
BlobKey blobKey = blobMap.get(UPLOAD_WIDJET_NAME);
UPLOAD_WIDJET_NAME is the .setName for the FileUpload widjet.
What you are doing there is getting a key for yout BLob so you can reference it later.
Our next step is showing the uploaded image back to the GWT layer:
//In the same post method from step 7
ImagesService imagesService = ImagesServiceFactory.getImagesService();
String imageURL = imagesService.getServingUrl(blobKey);
response.sendRedirect("/XXX/YYY?imgURL="+imageURL);
Now in the get method:
String imageUrl = request.getParameter("imgURL");
response.setHeader("Content-Type", "text/html");
response.getWriter().println(imageUrl);
We are done, now you just have to
uploadFormPanel.addSubmitCompleteHandler(new SubmitCompleteHandler() {
#Override
public void onSubmitComplete(SubmitCompleteEvent event) {
uploadFormPanel.reset();
initBlobStoreSession();
String imageUrl = event.getResults();
Image image = new Image();
image.setUrl(imageUrl);
//if you are using jetty, leave this on
//or else it wont work
//Don't use GWT.getModuleBaseURL(), it doesnt
//work well in development mode
imageUrl.replace("http://0.0.0.0:8888/", "");
System.out.println(imageUrl);
final PopupPanel imagePopup = new PopupPanel(true);
imagePopup.setWidget(image);
// Add some effects
imagePopup.setAnimationEnabled(true); // animate opening the image
imagePopup.setGlassEnabled(true); // darken everything under the image
imagePopup.setAutoHideEnabled(true); // close image when the user clicks
imagePopup.center(); // center the image
}
});
check out upload4gwt which address uploading in GWT on AppEngine.
(disclosure: I created upload4gwt; it's not mature yet, however may be useful)
I had the same problem. As a workaround I'm using a redirection to a servlet that print a status message for the client to parse.
I'm passing the websafe string representation of the key to that result servlet.
That's a bit hackey, I'd like someone to come with a better answer, or explain why the blobstore servlet have to redirect.
Yeah, things get more complicated with uploads in GWT.
You can save the form data and image in separate RPCs, and either include a status message in the response to the image upload, or fire off a 3rd RPC when the form returns to get any status or metadata you need.