Skip to content

Commit

Permalink
Serialize and deserialize with the correct encoding, fixes tomkuijste…
Browse files Browse the repository at this point in the history
  • Loading branch information
Jark committed May 16, 2017
1 parent 3d830b2 commit e626f51
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 104 deletions.
5 changes: 1 addition & 4 deletions src/HttpMessage/HttpServerResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,7 @@ public byte[] ToBytes()
public override string ToString()
{
#if DEBUG
// This is just used for debugging purposes and will not be available when running in release mode. Problem with
// this method is that it uses Encoding to decode the content which is a fairly complicated process. For debugging
// purposes I'm using UTF-8 which is working most of the time. In real life you want to use the charset provided, or
// some default encoding as explained in the HTTP specs.
// This is just used for debugging purposes and will not be available when running in release mode.
return HttpServerResponseParser.Default.ConvertToString(this);
#else
return $"{HttpVersion} {ResponseStatus} including {Headers.Count()} headers.";
Expand Down
5 changes: 3 additions & 2 deletions src/HttpMessage/ServerResponseParsers/ContentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Restup.HttpMessage.ServerResponseParsers
{
internal class ContentParser : IHttpResponsePartParser
{
private static Encoding DEFAULT_CONTENT_ENCODING = Encoding.UTF8;
private static readonly Encoding DEFAULT_CONTENT_ENCODING = Encoding.UTF8;

public byte[] ParseToBytes(HttpServerResponse response)
{
Expand All @@ -17,7 +17,8 @@ public string ParseToString(HttpServerResponse response)
if (response.Content == null)
return string.Empty;

return DEFAULT_CONTENT_ENCODING.GetString(response.Content);
var encoding = Encoding.GetEncoding(response.ContentCharset);
return (encoding ?? DEFAULT_CONTENT_ENCODING).GetString(response.Content);
}
}
}
17 changes: 17 additions & 0 deletions src/WebServer.UnitTests/Rest/FluentRestRouteHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Restup.Webserver.Rest;
using Restup.Webserver.UnitTests.TestHelpers;

namespace Restup.Webserver.UnitTests.Rest
{
public class FluentRestRouteHandlerTests : FluentHttpServerTests<FluentRestRouteHandlerTests>
{
public FluentRestRouteHandlerTests ControllersIsRegistered<T>(string urlPrefix, params object[] arguments) where T : class
{
var restRouteHandler = new RestRouteHandler();
restRouteHandler.RegisterController<T>(arguments);
RegisterRouteHandler(urlPrefix, restRouteHandler);

return this;
}
}
}
134 changes: 134 additions & 0 deletions src/WebServer.UnitTests/Rest/RestRouteHandlerTests.XmlAcceptCharset.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Restup.HttpMessage.Headers.Request;
using Restup.HttpMessage.Models.Schemas;
using Restup.Webserver.Attributes;
using Restup.Webserver.Models.Contracts;
using Restup.Webserver.Models.Schemas;
using Restup.Webserver.UnitTests.TestHelpers;

namespace Restup.Webserver.UnitTests.Rest
{
[TestClass]
public class RestRouteHandlerTests_XmlAcceptCharset : RestRouteHandlerTests
{
[TestMethod]
[DataRow("utf-8")]
[DataRow("utf-16")]
public void WhenAGetRequestWithAcceptMediaTypeXmlAndAnAcceptEncodingIsReceived_ThenTheResponseIsCorrectlyEncoded(string charset)
{
new FluentRestRouteHandlerTests()
.Given
.ControllersIsRegistered<XmlTestController>("/api", new ProductStore())
.When
.RequestHasArrived("/api/products",
acceptCharsets: new[] { charset },
acceptMediaTypes: new[] { "application/xml" })
.Then
.AssertLastResponse(x => x.ContentCharset, charset)
.AssertLastResponseContent($"<?xml version=\"1.0\" encoding=\"{charset}\"?><Product xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><Id>{Product.DefaultId}</Id><Name>{Product.DefaultName}</Name><Currency>{Product.DefaultCurrency}</Currency></Product>");
}

[TestMethod]
[DataRow("utf-8")]
[DataRow("utf-16")]
public void WhenAPostRequestWithAcceptMediaTypeXmlAndAnAcceptEncodingIsReceived_ThenTheRequestIsCorrectlyReceived(string charset)
{
var productStore = new ProductStore();
var id = 2;
var name = "new name";
var currency = "£";

var encoding = Encoding.GetEncoding(charset);

new FluentRestRouteHandlerTests()
.Given
.ControllersIsRegistered<XmlTestController>("/api", productStore)
.When
.RequestHasArrived("/api/products",
method: HttpMethod.POST,
acceptCharsets: new[] { charset },
acceptMediaTypes: new[] { "application/xml" },
contentType: "application/xml",
contentCharset: charset,
content: encoding.GetBytes($"<?xml version=\"1.0\" encoding=\"{charset}\"?><Product xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><Id>{id}</Id><Name>{name}</Name><Currency>{currency}</Currency></Product>"))
.Then
.AssertLastResponseHasNoHeaderOf<ContentTypeHeader>()
.AssertLastResponse(x => x.ResponseStatus, HttpResponseStatus.Created);

Assert.AreEqual(1, productStore.PostedProducts.Count);

var postedProduct = productStore.PostedProducts.Single();
Assert.AreEqual(id, postedProduct.Id);
Assert.AreEqual(name, postedProduct.Name);
Assert.AreEqual(currency, postedProduct.Currency);
}

[RestController(InstanceCreationType.PerCall)]
public class XmlTestController
{
private readonly ProductStore productStore;

public XmlTestController(ProductStore productStore)
{
this.productStore = productStore;
}

[UriFormat("/products")]
public IGetResponse GetProduct()
{
return new GetResponse(GetResponse.ResponseStatus.OK, productStore.Get());
}

[UriFormat("/products")]
public IPostResponse PostProduct([FromContent] Product product)
{
productStore.Post(product);
return new PostResponse(PostResponse.ResponseStatus.Created);
}
}

public class ProductStore
{
public ConcurrentQueue<Product> PostedProducts = new ConcurrentQueue<Product>();

public Product Get()
{
return new Product(Product.DefaultId, Product.DefaultName, Product.DefaultCurrency);
}

public void Post(Product product)
{
PostedProducts.Enqueue(product);
}
}

public class Product
{
public const int DefaultId = 1;
public const string DefaultName = "default";
public const string DefaultCurrency = "£";

public int Id { get; set; }
public string Name { get; set; }
public string Currency { get; set; }

// xml serialisation
private Product()
{

}

public Product(int id, string name, string currency)
{
Id = id;
Name = name;
Currency = currency;
}
}
}
}
Loading

0 comments on commit e626f51

Please sign in to comment.