Browsed by
Month: December 2015

Handling images WebAPI (+examples)

Handling images WebAPI (+examples)

WebAPI is used to serve data from server. One time of data could be images. In this post I will show how to write WebAPI methods operating on images data.

So at first we must decide where we want to store our images. Let’s suppose, that we have database that store our application data. We have 2 possibilities: to store images in files or directly in database. This two approaches has following adventages:

  • files
    • easy to test
    • fast
    • requires additional backup strategy
  • database
    • scalable
    • could be joined with db objects (no name conflicts)
    • transactional integrity
    • easy to apply permissions

Based on this points, you can see that storing images in database could be preferable when image is part of entities in your system i.e. photo of product. So we focus on this type of storing. You can easily change.

Handling images

GET

[HttpGet]
[Route("{id}/image")]
public HttpResponseMessage GetImage(int id)
{
	var image = _service.GetImage(id);

	MemoryStream ms = new MemoryStream();
	HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
	result.Content = new ByteArrayContent(image);
	result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
	return result;
}

As you can see, there is a MemoryStream, that returns image data to response. Also we must add ContentType header to define type of our image.

DELETE

This method is simple and trival

[HttpDelete]
[Route("{id}/image")]
public void DeleteImage(int id)
{
	_service.Remove(id);
}

POST

This part is a bit tricky at first you need to read multipart stream and then join each of part to one byte array. This array contains final image data that could be saved to database.

[HttpPost]
[Route("{id}/image")]
public async Task<HttpResponseMessage> PostImage(int id)
{
	if (Request.Content.IsMimeMultipartContent())
	{
		string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
		MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider(fullPath);
		var task = await Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
		{
			if (t.IsFaulted || t.IsCanceled)
				throw new HttpResponseException(HttpStatusCode.InternalServerError);
			MemoryStream ms = new MemoryStream();
			foreach (var file in streamProvider.FileData)
			{
				var info = new FileInfo(file.LocalFileName);
				var data = File.ReadAllBytes(info.FullName);
				ms.Write(data, 0, data.Length);
			}
			_service.UpdateImage(ms.ToArray());
			return new HttpResponseMessage(HttpStatusCode.OK);
		});
		return task;
	}
	else
	{
		throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
	}
}
Interceptor in Angular

Interceptor in Angular

$http service

This service is an Angular method for making HTTP request for external resources. All HTTP requests has an asynchronous nature. Because of that, all operations return promises. Generally, this service could be used by defining parameters directly, like below

$http({
  method: 'GET',
  url: '/someUrl'
}).then(function successCallback(response) {
    // this callback will be called asynchronously
    // when the response is available
  }, function errorCallback(response) {
    // called asynchronously if an error occurs
    // or server returns response with an error status.
  });

or by using shortcut methods

$http.get('/someUrl', config).then(successCallback, errorCallback);

Interceptor in Angular

The same as in previous post about interceptor in Angular you can also setup global mechanism to intercept each HTTP request. It can be useful for error handling, logging or authentication. You can use 4 types of additional configurations:

  • before sending request (request function)
  • after response retrieve (response function)
  • on error during sending request (requestError function)
  • on error during retrieving response (responseError function)

Implementation

To setup interceptor, you should only define service factory with declaring at least one of function mentioned in list above. Below you can see an example from the Angular documentation

// register the interceptor as a service 
$provide.factory('myHttpInterceptor', function($q) 
{ 
    return { 
        'request': function(config) { 
            // do something on success 
            return config || $q.when(config);
        }, 
        'requestError': function(rejection) { 
            // do something on error 
            if (canRecover(rejection)) { 
                return responseOrNewPromise 
            } 
            return $q.reject(rejection); 
        }, 
        'response': function(response) { 
            // do something on success 
            return response || $q.when(response); 
        }, 
        'responseError': function(rejection) { 
            // do something on error 
            if (canRecover(rejection)) { 
                return responseOrNewPromise 
            } 
            return $q.reject(rejection); 
        } 
    }; 
}); 
$httpProvider.interceptors.push('myHttpInterceptor');

Into this handlers you can also use asynchronous operations, because you can return promises from them.

As can be seen, it is very simple, but powerful functionality to add common behaviours to your data operations.

References:

https://docs.angularjs.org/api/ng/service/$http

http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/

http://www.bennadel.com/blog/2777-monitoring-http-activity-with-http-interceptors-in-angularjs.htm

http://thecodebarbarian.com/2015/01/24/angularjs-interceptors

Zapytania warunkowe oparte na sumach kontrolnych

Zapytania warunkowe oparte na sumach kontrolnych

W tym przypadku stawiane warunki są oparte na pewnym kluczu dla zasobu. Powinien on być silnym walidatorem, a więc zmieniać się za każdym razem, gdy zasób ulegnie zmianie.

Definiuje się go za pomocą nagłówka ETag:

Response
Cache-Control:public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"

Gdy klient chce ponownie pobrać dany zasób, informuje serwer o ETagu aktualnie przechowywanym w cache’u. Gdy ten się zmieni należy ponownie przesłać treść zasobu.

Request
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"

W tym przypadku istnieją 3 użyteczne nagłówki:

  • If-Match – używane do warunkowego wykonywania zapytań modyfikujących dane dla upewnienia się, że rekord się nie zmienił,
  • If-None-Match – używane do warunkowego pobierania zasobu, gdy przechowujemy go już w cache (patrz przykład wyżej),
  • If-Range – pozwala pobrać pewien zakres zasobu (w połączeniu z nagłówkiem Range), jednak tylko gdy jego przechowywana wersja jest nadal aktualna. Ten nagówkek współpracuje zarówno z datą jak i ETagiem.

 

 

Cache oparty na czasie

Cache oparty na czasie

Walidatory

Wyróżniamy 2 rodzaje walidatorów zasobów:

  • silne – występują wtedy, gdy każda zmiana zawartości zasobu powoduje wykrycie zmiany w walidatorze
  • słabe – gdy wykrywanie zmian w walidatorze opiera się na danych niezależnych od wartości zasobu (np. na informacjach o czasie)

Zgodnie z powyższą definicja wyróżniamy dwa rodzaje cache’a:

Cache oparty na czasie

Polega on na tym, że przechowywany zasób może być używany tylko przez zdefiniowany czas. Do tego celu używany jest nagłówek: Last-Modified, który wskazuje na datę ostatniej modyfikacji zasobu.

Last-Modified: Tue, 15 Nov 2015 12:45:26 GMT

Data ta jest zapamiętywana razem z rekordem i podczas wysłania następnego zapytania GET lub HEAD, dodawany jest warunkowy nagłówek sprawdzający, czy data ta się zmieniła

If-Modified-Since: Tue, 15 Nov 2015 12:45:26 GMT

Jeżeli zasób nie został od tego czasu zmieniony zwrócony zostanie zasób przechowywany w cache ze statusem 304 (Not Modified)

Nagłówek ten jest używany w następujących przypadkach:

  • aby umożliwić działanie cache opartym na wartościach czasowych
  • aby ograniczyć zakres przeglądania zasobów sieciowych tylko do rekordów zmienionych od czasu poprzedniego zapytania

Istnieje także nagłówek If-Unmodified-Since, który powoduje przesłanie treści zasobu tylko gdy nie został on zmodyfikowany od pewnego momentu. Nie jest on bezpośrednio wykorzystywany przez cache, ale ma 2 zastosowania:

  • gdy pobieramy pewien zasób partiami, przy pobieraniu kolejnej cześć chcemy się upewnić, czy nie został on zmodyfikowany i pobieramy cały czas ten sam zasób
  • dla zapytań modyfikujących rekord (np. PUT) możemy w ten sposób zaimplementować mechanizm sprawdzania czy rekord nie został w międzyczasie przz kogoś zmieniony i czy nie chcemy zaktualizować go bazując na nieaktualnej wersji. Jest to rozwiązanie problemu równoczesnego wykorzystywania zasobów.
Nagłówki cache

Nagłówki cache

Age

W przypadku, gdy jako wynik zapytania jest zwracany zasób przechowywany w cache, nagłówek ten mówi nam, ile czasu (w sekundach) minęło od pobrania treści rekordu.

Cache-Control

Jest on stosowany do sterowania zachowaniem cache. Może posiadać niżej wymienione parametry:

max-age – wskazuje jak długi czas życia (w sekundach) przechowywanego zasobu jest akceptowalny; może być używany zarówno w zapytaniach jak i odpowiedziach. Ustawienie max-age: 0 powoduje, że zasób jest dynamiczny i nie podlega cache’owaniu. Jednak nie powinniśmy tego nadużywać, bo dla większości zasobów jest możliwe ustalenie pewnego okresu trwałości, choćby 10 sekund. Nawet dla zasobów, wymagających pełnej aktualności.

s-maxage – działa podobnie jak max-age, przy czym odwołuje się do współdzielonego cache’u.

max-stale – oznacza, że klient jest gotowy na odczytanie z cache’u zasobu, który jest nieaktualny przez maksymalną zdefiniowaną ilość sekund. Pozwala na zdefiniowanie dodatkowego czasu używania zasobu z cache.

min-fresh – pozwala pobrać wartość, która będzie jeszcze aktualna co najmniej przez określoną ilość sekund.

no-cache – informuje, że do wykonania tego zapytania nie powinien być wykorzystany cache.

no-store – informuje, że żaden fragment odpowiedzi nie powinien być zapisywany w cache.

no-transform – oznacza, że serwery pośrednie nie powinny przekształcać tej wiadomości. Nie powinny też próbować korzystać z cache dla tego rekordu, ani dodawać informacji o aktualności rekordu (Age). Dodatkowo niektóre serwery proxy mogą chcieć dokonywać transformacji przesyłanych treści, np. kompresji obrazów. Ta opcja zakazuje tego typu modyfikacji. Jest to przydatne do przesyłania danych wrażliwych, np. w czasie logowania.

only-if-cached – klient chce dostać tylko dane znajdujące się w cache’u. Odpowiedzią może być rekord pobrany z cache lub kod 504 (Gateway Timeout), gdy cache dla tego zasobu jest pusty.

must-revalidate – oznacza, że odkąd rekord w cache stanie się nieaktualny, nie może on już być więcej użyty. Jest to użyteczne do zapewnienia wiarygodnych operacji zgodnych z ustawieniami daty ważności. Służy zwykle do wymuszenia odświeżenia rekordu niezależnie od ustawień klienta.

public – oznacza, że request nie jest wrażliwy i może być przechowywany we współdzielonym cache’u.

private – zapytanie i odpowiedź mogą być tylko przechowywane i udostępniane w obrębie tego samego użytkownika. Nie zapewnia to jednak bezpieczeństwa przechowywanych danych i nadal informacje autentykacyjne nie powinny być cache’owane.

proxy-revalidate – działa tak samo jak must-revalidate, ale tylko dla współdzielonego cache’u.

Poniżej widać przypisanie, które opcje mogą być użyte w zapytaniach, a które w odpowiedziach.

Request Response
no-cache public
no-store private
max-age no-cache
max-stale no-store
min-fresh no-transform
no-transform must-revalidate
only-if-cached proxy-revalidate
max-age
s-maxage

Przykłady:

Cache-Control:public, max-age=31536000
Powyższy nagłówek oznacza cache ważny rok czasu od momentu pobrania

Cache-Control:public
Expires: Mon, 25 Jun 2012 21:31:12 GMT

Ten nagłówek wskazuje dokładną datę wygaśnięcia cache’owanego elementu

Expires

Wskazuje datę i godzinę, po której odpowiedź jest traktowana jako zdezaktualizowana. Wartość max-age z nagłówka Cache-Control jest ważniejsza niż Expires i nadpisuje ją.

Pragma

Nagłówek ten służy głównie do zapewnienia wstecznej kompatybilności ze standardem HTTP 1.0. Pozwala na zdefiniowanie dodatkowych właściwości dla zapytania. W naszym przypadku interesująca jest tylko wartość no-cache, która działa identycznie jak no-cache z Cache-Control.

Należy zauważyć, że w przypadku wystąpienia nagłówka Cache-Control i przetwarzania zapytania w standardzie HTTP 1.1, nagłówek Pragma jest ignorowany.

Może to prowadzić sytuacji, że definiując dwa sprzeczne nagłówki, powodujemy różne zachowanie naszego zapytania zależnie od obsługującej go wersji HTTP. Należy o tym pamiętać podczas używania Pragma.

GET / HTTP/1.1
Host: www.example.com
Cache-Control: max-age=30
Pragma: no-cache

Warning

Nagłówek ten może przekazywać dodatkowe informacje na temat transformacji, jakim podlegała wiadomość. Jest to ostrzeżenie, że wiadomość wysłana różni się od otrzymanej.

Ostrzeżenia są podobne do statusów, jednak mogą przekazywać więcej informacji i mogą występować wielokrotnie w jednej wiadomości. Na przykład:

Warning: 110 - "Response is Stale"
Warning: 113 - "Heuristic Expiration"

Źródła:
http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/
https://www.mnot.net/cache_docs/
https://tools.ietf.org/html/rfc7234