❔ Error with showing Images in Azure App Service.
Using Asp.net and Azure Blob Storage and Azure App Service:
When I run my API locally on my computer I can receive Images. even if they have slashes, indicating that they are part of a directory:
the same code and the same request fails to run when my API is published to the App service.
Instead I get a 404 when trying to request Images with slashes (like the one on top). Images that do not have backslashes in the name instead work normally tho.
the endpoint that retrieves the images is the following.
I suspect that, because the directory symbol is the same as a web-directory, my API fails to show the image. I do not believe that loading the actual image from blob storage is the problem, as images without slashes load normally.
Also if I type in a random name of an image that does not exist I explicitly get a Bad Request stating that the image does not exist, instead of a 404.
Any ideas why that behavior may happen?
204 Replies
I'd try a minimal reproduction, are you using azure storage locally, or a the local file system, or azurite?
ok im lost already
you said, locally it works
I just upload my Images to a Azure storage account
what do you mean by locally it works?
When i run it locally I still get the images from the remote Storage account
kk
azurite is an emulator
are you sure UrlDecode is doing what you expect?
I'm assuming _fileService is a bare facade over blob storage
I had a problem where the url was encoded, causing / to be converted to %2F i think. So i added that to decode it. Im not sure if its doing the same when not done locally but i would asume it does
I can show you the file service if you want. It just handles all the interaction with the storage account so I dont have to do that in the controller.
sure why not
ah i cant
Ill upload the relevant method
it just opens the blob and returns a result object
And the thing is, that if I type in the name of a blob that does not exist I get a Bad request instead of a 404
Yeah I'm struggling with that part
I'm looking at the docs
ok
You've got some likely false assumptions in your exceptions btw
what do you mean
catching the super-exception is frequently a bad idea
at this level
okay what would be recomended?
if a blob fails to download a RequestFailedException will be thrown
maybe you should catch that
and try again?
RequestFailedException Class (Azure) - Azure for .NET Developers
An exception thrown when service request fails.
it depends
it's kind of a complex area
Ah you want me to catch that exception in particular?
for example
ideally you only catch what you can handle
so anything that goes wrong you catch and then blind back up.
ok I did
ill implement retry logic later once i read up on the topic some more, I do not want to be stuck in a retry loop
nah I get that
you don't want to retry forever
for now I just want to be able to see the same behavior on my App service as in development
(when running the backend in memory on my pc over localhost)
change to...
catch (Exception ex) when (ex is rfe RequestFailedException)
then you'll have rfe as a reference to RequestFailedException in your catchblockok
so, if it's a status code of 404, it's not found
yeah
for example
but if an image is not found my code will return a bad requestr
so the 404 does not originate from my code right?
exceptions will cause 500 if im not mistaken
let me look at code again
ok
Im thinking its something with routing not set up correctly in azure
I was about to say that
ok I have no clue when it comes to azure i rarely use it.
I don't know how friendly
[HttpGet("{fileName}")]
public async Task<IActionResult> Download(string fileName)
{
isah yeah wildcard is better I think no?
I tried both
I'm not an expert at routing
honestly, using file path separators is probably an issue
is there a requirement that you do so?
I could use a different seperator then change it to / to get the file but that sounds scuffed
I'd personally have to reserach routing behavior to see how something like....
/[controller]/test/test/test/test.png ends up being routed
becuase the files have / as seperators
NB: blob storage doesn't actually create folders with /'s
it creates directories
most storage ux treats the / as organization unit
yeah
thats right
but blog storage itself doesn't know about directories
yeah yeah
but question
how are the files getting there?
I upload them with another endpoint
you do, or users do?
users do
but I choose the path
are you validating the files at all?
I am
kk
I wrote a validator for that
otherwise you're building a malware delivery platform 🙂
discord is dealing with that currently
lol yeah ik
it just takes pngs and looks that they are 124 x 124
ah so you actually insepct it's a png, that's mostly good
png is usually a safe format iirc, you can't easily embed in RCE type code
I think you're issue is somehwere here...
where either routing isn't doing as you expect or UrlDecode isn't doing as you expect
hm.
but wouldnt that just result in a Bad Request instead
like its tries to look up a nonsense file and just finds nothing
you might want to createa dummy route in azure that looks like this...
I don't know who wins in that scenario, iirc for routing, specificity wins, so if your path is /[controller]/check/blah/blah/blah.png you should get back
blah/blah/blah.png
That's kind of a hinky way to do it with redeploying and all, but it's probably your shortest path to checkok ill try
wait
I just verified that
System.Net.WebUtility.UrlDecode("%2fblah%2fblah%2fblah.png"); is
/blah/blah/blah.png
Glancing at your code you don't seem to have the ability to really determine if the blob is there or not
from the controller perspective
I do not want to give to much inside outside of success or no success
well I guess you are sending back a message too
hm okay
what would you change then?
oh my controller code was wrong
yea
casing
ah i was losing my mind over here lol
[HttpGet("check/{filename}")]
public async Task<IActionResult> Download(string filename)
{
fileName = System.Net.WebUtility.UrlDecode(filename);
return Ok(fileName);
}
not fileName
i see isee
some of it is how you percieve success, 404 is an okay status to return, you asked for this file, it's not there.
yeah but the way I handle errors currently dont support multiple stati
its either it worked or it didnt
I googled to find a better way of handling errors but it doesnt seem worth it
yeah it was worth checking, I expected it would
yep yep : )
so there's something going on I'm not seeing in the code, or something else is happening in azure
it all falls back on the BusinessLogicMessage
this is what I use when i run some kind of business logic.
litterally every service i have uses this as the return type
On the surface, your controller looks like it should do what it should
like i could change the enum to the HTTP codes but then again handling all the cases from the controller sounds bothersome
do you have azcopy ?
no
Copy or move data to Azure Storage by using AzCopy v10
AzCopy is a command-line utility that you can use to copy data to, from, or between storage accounts. This article helps you download AzCopy, connect to your storage account, and then transfer data.
its so you can mirror my setup?
nah
you can try and use azcopy to get the same blob
unless that's what you mean by mirror settup
no no
so you should be able to use azcopy to pull the same blob
you can use storage explorer too
but storage explorer is possibly too friendly for a check
I used IAM or what ever that is called to authorize both client and app service i hope thats not an issue
it's like literally azcopy
storageaccounturl
localfile
I mean if you have rbac set up correctly it should be okayok
wait do they always have urls?
which?
blob storage?
every blob has a url
ah the containers do
okok
yeah
I mean you can eyeball it too, but everything looks kosher right now
yeah
I might have coded some thing different
I mean I can pull the files
locally
using the backend so I dont think its anything with the storage account
what ASP are you using?
7.0 i think
err
ehh
not goodß
?
app services run in an app service plan
yep
so your app service plan, what is the OS for instance?
linux
wait is that the problem?
are you using linux locally?
no
windows locally
potentially
yeah they use backslash
linux cares about some things windows doesn't file case, etc.
I mean what you're calling filename is just a string
hm but would linux or windows inpact routing
nah
at least, I'm asying nah 🙂
ok
obviously your routing is working
we proved that
but what we both agree on is that the 404 is thown buy the Azure app service somehow
and while I don't like your error handling, your blob code looks reasonable
yeah i dont like it either lo ll
the thing that would care about case would really be the azure storage resource itself
it's an http call at the end of the day
does it consistently fail?
yes
anything with a slash doesnt work
but without it works
if I run it locally it always works
and we verified that the input is correct
like what our controller is passing into my service
I don't know of any subtleties between linux and windows with asp.net core and the code you've shown
hm
I could change the controller to just pass in a string over a model
I'm gonna try and repro real quick
yeah
while you do that i do what i suggested
okay lol
this works
its litterally just the weird route
but that makes no sense to me
okay I have a working thing locally
ok
btw did you see? it works on azure aswell. the only difference is the route
haven't tried azure yet
creating an empty weba pp
ok
web deploying now
this is a frigid startup
nm cold start
ok
must be the f tier really sucks
yeah
I think we can test the route seperately
im going to try that
if so we dont need to bother with blobs at all
hhhhhhmmmmmmm
I'm getting a 404
lemme check kudu
ok
okay lemme add a non slash blob
yep
Okay
It doesnt work in general
this doesnt work on azure aswell
switch to a windows asp
ok ill try
wait wha
Does it work?
on windows?
NAH
well wait
that image has no slashes
ah
404 yeah
but works locally
yeah
i swap to windows real quick I hope I wont have any problems unrelated to this
swapping back is easy
ye
NB: windows asp's are more expensive
I'm making a change and redeploying
to windows?
nah
ok
how do I swap i only have the option to create a new app
you probably have to use az cli
i got this
x86?
win-x64
ok
ai windows does not support 7.0
like what i showed?
can you show me the code?
ok
looks like a fine alternative no?
i just use query strings
yah
or you could probably build a custom route handler
eh i got other things to do
x)
I need to build a chat system
too
all you'd do is drop the "{filename}" from HttpGet()
and change filename [FromQuery]string filename
yep
Thanks for helping
atleast I got some reassurance that it truly is a weird problem
yeah it's a bit weird and I'm sure some folks better versed in webapi could say "oh, just add this"
I'm going to deploy to windows asp
ok but dont bother too much.
Im just going to use the query stuff : ) )
unless you are yourself are interested
https://wincheesediscord.azurewebsites.net/WeatherForecast/blah/blah/blah.png
doesn't work eitehr
on windows
damn
and works local
could be a gateway issue
yeah
this is a known issue
like with azure app service in general?
yeah
there is a pendning fix
ffs
I spend valuable sleep on this
there are work arounds but they all sorta suck
query parameter it is
@Azazel https://github.com/Azure/azure-functions-host/issues/9290 if you want to see more about it
and the pending fix... https://github.com/Azure/azure-functions-host/pull/9402
maybe it is merged
This is an opt-in feature customers needs to explicitly opt-in by setting the WEBSITE_RESTORE_RAW_REQUEST_PATH app setting.
yeah no I will not do that
Ill just wait till its merged
yeah that settings isn't working for me
small indie company
@🎄Mayor McCheese 🎄 Oi sorry to bother you.
I remembered that you told me my error handling was not so good.
Any pointers you could show me to improve or maybe some designpatterns that would allow all Internal logic to be handled in services. I want to be able to create a IActionResult from my BusinessLogicMessage.
asp.net core has a "global exception handler"
that's where you want to catch "exception"
so, ideally you only catch what you can handle
so my services should only return what I need
and error handling will happen globalyß
?
for example, cosmos database throws an exception when documents aren't found
so
Ahh
that way I only handle each exeption once
so in the above
we can "handle" a 404 by returning a new empty document
so, a frequent anti pattern you'll see is...
so in the code above, you'll get two log entries for the same error
so probably don't do that
now consider you have a console app that does some work...
so in our main worker loop, we'll catch any exception assuming it's a transient falure, wait and then continue again
we don't want to keep retrying forever though
so if our error count surges then we'll shut down the app
they are all pretty naive examples
yep
they all have some flaws
but the idea is that you want to catch specific exceptions that you can address the issues with
otherwise just let the error bubble up
if I let the exeptions be handled globaly, I dont have to care for edgecases and can handle the edgecases on a per exception basis right?
basically
though I don't like the word edgecase
it's possible with cosmosdb for instance, that you'll ask for document that doesn't exist.
returning a new document isn't an unreasonable reaction
ok
I get it
in your image case
I rip that fucking BusinessLogicMessage out
what is the image doesn't exit?
if it doesnt exist ill return either null
return an empty image
what about?
ye
but if it cant find the container i let the exeption fire
and handle it globaly
if that suits your code, then yeah
ok : )
thanks a lot
im really thankfull of all the help
so for an HttpClient for example
if you get a 500 what should you do?
a retry is probably reasonable
yeah
but you don't want to keep retrying
there's some libraries like https://github.com/App-vNext/Polly
GitHub
GitHub - App-vNext/Polly: Polly is a .NET resilience and transient-...
Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and ...
which help with things like retries and fallbacks
basically what you did in the 3. example just better
and as a library
the non naive appoach
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.