Skip to content

v2.0.8 #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ deploy:
server: https://www.myget.org/F/research-institute/api/v2/package
api_key:
secure: 6CeYcZ4Ze+57gxfeuHzqP6ldbUkPtF6pfpVM1Gw/K2jExFrAz763gNAQ++tiacq3
skip_symbols: true
skip_symbols: false
on:
branch: develop
- provider: NuGet
server: https://www.myget.org/F/jadnc/api/v2/package
api_key:
secure: 6CeYcZ4Ze+57gxfeuHzqP6ldbUkPtF6pfpVM1Gw/K2jExFrAz763gNAQ++tiacq3
skip_symbols: true
skip_symbols: false
on:
branch: unstable
- provider: NuGet
Expand Down
52 changes: 52 additions & 0 deletions src/JsonApiDotNetCore/Controllers/HttpMethodRestrictionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Internal;
using Microsoft.AspNetCore.Mvc.Filters;

namespace JsonApiDotNetCore.Controllers
{
public abstract class HttpRestrictAttribute : ActionFilterAttribute, IAsyncActionFilter
{
protected abstract string[] Methods { get; }

public override async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
var method = context.HttpContext.Request.Method;

if(CanExecuteAction(method) == false)
throw new JsonApiException("405", $"This resource does not support {method} requests.");

await next();
}

private bool CanExecuteAction(string requestMethod)
{
return Methods.Contains(requestMethod) == false;
}
}

public class HttpReadOnlyAttribute : HttpRestrictAttribute
{
protected override string[] Methods { get; } = new string[] { "POST", "PATCH", "DELETE" };
}

public class NoHttpPostAttribute : HttpRestrictAttribute
{
protected override string[] Methods { get; } = new string[] { "POST" };
}

public class NoHttpPatchAttribute : HttpRestrictAttribute
{
protected override string[] Methods { get; } = new string[] { "PATCH" };
}

public class NoHttpDeleteAttribute : HttpRestrictAttribute
{
protected override string[] Methods { get; } = new string[] { "DELETE" };
}
}
9 changes: 9 additions & 0 deletions src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using JsonApiDotNetCore.Internal;
using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc.Formatters;
Expand Down Expand Up @@ -50,6 +52,13 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
context.HttpContext.Response.StatusCode = 422;
return InputFormatterResult.FailureAsync();
}
catch(JsonApiException jex)
{
_logger?.LogError(new EventId(), jex, "An error occurred while de-serializing the payload");
context.HttpContext.Response.StatusCode = jex.GetStatusCode();
context.HttpContext.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jex.GetError())));
return InputFormatterResult.FailureAsync();
}
}

private string GetRequestBody(Stream body)
Expand Down
13 changes: 12 additions & 1 deletion src/JsonApiDotNetCore/Internal/JsonApiExceptionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,19 @@ public static JsonApiException GetException(Exception exception)
case "InvalidCastException":
return new JsonApiException("409", exception.Message);
default:
return new JsonApiException("500", exception.Message);
return new JsonApiException("500", exception.Message, GetExceptionDetail(exception.InnerException));
}
}

private static string GetExceptionDetail(Exception exception)
{
string detail = null;
while(exception != null)
{
detail = $"{detail}{exception.Message}; ";
exception = exception.InnerException;
}
return detail;
}
}
}
27 changes: 19 additions & 8 deletions src/JsonApiDotNetCore/Internal/TypeHelper.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
using System;
using System.Reflection;

namespace JsonApiDotNetCore.Internal
{
public static class TypeHelper
{
public static object ConvertType(object value, Type type)
{
if(value == null)
return null;
try
{
if (value == null)
return null;

type = Nullable.GetUnderlyingType(type) ?? type;
type = Nullable.GetUnderlyingType(type) ?? type;

var stringValue = value.ToString();

if(type == typeof(Guid))
return Guid.Parse(stringValue);
var stringValue = value.ToString();

return Convert.ChangeType(stringValue, type);
if (type == typeof(Guid))
return Guid.Parse(stringValue);

if (type == typeof(DateTimeOffset))
return DateTimeOffset.Parse(stringValue);

return Convert.ChangeType(stringValue, type);
}
catch (Exception e)
{
throw new FormatException($"{ value } cannot be converted to { type.GetTypeInfo().Name }", e);
}
}
}
}
2 changes: 1 addition & 1 deletion src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>2.0.7</VersionPrefix>
<VersionPrefix>2.0.8</VersionPrefix>
<TargetFrameworks>netstandard1.6</TargetFrameworks>
<AssemblyName>JsonApiDotNetCore</AssemblyName>
<PackageId>JsonApiDotNetCore</PackageId>
Expand Down
79 changes: 50 additions & 29 deletions src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class JsonApiDeSerializer : IJsonApiDeSerializer
private readonly IJsonApiContext _jsonApiContext;
private readonly IGenericProcessorFactory _genericProcessorFactor;

public JsonApiDeSerializer(
public JsonApiDeSerializer(
IJsonApiContext jsonApiContext,
IGenericProcessorFactory genericProcessorFactory)
{
Expand All @@ -26,9 +26,16 @@ public JsonApiDeSerializer(

public object Deserialize(string requestBody)
{
var document = JsonConvert.DeserializeObject<Document>(requestBody);
var entity = DocumentToObject(document.Data);
return entity;
try
{
var document = JsonConvert.DeserializeObject<Document>(requestBody);
var entity = DocumentToObject(document.Data);
return entity;
}
catch (Exception e)
{
throw new JsonApiException("400", "Failed to deserialize request body", e.Message);
}
}

public object Deserialize<TEntity>(string requestBody)
Expand All @@ -38,26 +45,40 @@ public object Deserialize<TEntity>(string requestBody)

public object DeserializeRelationship(string requestBody)
{
var data = JToken.Parse(requestBody)["data"];
try
{
var data = JToken.Parse(requestBody)["data"];

if(data is JArray)
return data.ToObject<List<DocumentData>>();
if (data is JArray)
return data.ToObject<List<DocumentData>>();

return new List<DocumentData> { data.ToObject<DocumentData>() };
return new List<DocumentData> { data.ToObject<DocumentData>() };
}
catch (Exception e)
{
throw new JsonApiException("400", "Failed to deserialize request body", e.Message);
}
}

public List<TEntity> DeserializeList<TEntity>(string requestBody)
{
var documents = JsonConvert.DeserializeObject<Documents>(requestBody);
try
{
var documents = JsonConvert.DeserializeObject<Documents>(requestBody);

var deserializedList = new List<TEntity>();
foreach (var data in documents.Data)
{
var entity = DocumentToObject(data);
deserializedList.Add((TEntity)entity);
}

var deserializedList = new List<TEntity>();
foreach (var data in documents.Data)
return deserializedList;
}
catch (Exception e)
{
var entity = DocumentToObject(data);
deserializedList.Add((TEntity)entity);
throw new JsonApiException("400", "Failed to deserialize request body", e.Message);
}

return deserializedList;
}

private object DocumentToObject(DocumentData data)
Expand All @@ -66,7 +87,7 @@ private object DocumentToObject(DocumentData data)
_jsonApiContext.RequestEntity = contextEntity;

var entity = Activator.CreateInstance(contextEntity.EntityType);

entity = SetEntityAttributes(entity, contextEntity, data.Attributes);
entity = SetRelationships(entity, contextEntity, data.Relationships);

Expand Down Expand Up @@ -106,8 +127,8 @@ private object SetEntityAttributes(
}

private object SetRelationships(
object entity,
ContextEntity contextEntity,
object entity,
ContextEntity contextEntity,
Dictionary<string, RelationshipData> relationships)
{
if (relationships == null || relationships.Count == 0)
Expand All @@ -117,18 +138,18 @@ private object SetRelationships(

foreach (var attr in contextEntity.Relationships)
{
entity = attr.IsHasOne
? SetHasOneRelationship(entity, entityProperties, attr, contextEntity, relationships)
entity = attr.IsHasOne
? SetHasOneRelationship(entity, entityProperties, attr, contextEntity, relationships)
: SetHasManyRelationship(entity, entityProperties, attr, contextEntity, relationships);
}

return entity;
}

private object SetHasOneRelationship(object entity,
PropertyInfo[] entityProperties,
RelationshipAttribute attr,
ContextEntity contextEntity,
private object SetHasOneRelationship(object entity,
PropertyInfo[] entityProperties,
RelationshipAttribute attr,
ContextEntity contextEntity,
Dictionary<string, RelationshipData> relationships)
{
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.InternalRelationshipName}Id");
Expand All @@ -142,7 +163,7 @@ private object SetHasOneRelationship(object entity,
{
var relationshipAttr = _jsonApiContext.RequestEntity.Relationships
.SingleOrDefault(r => r.PublicRelationshipName == relationshipName);

var data = (Dictionary<string, string>)relationshipData.ExposedData;

if (data == null) return entity;
Expand All @@ -159,9 +180,9 @@ private object SetHasOneRelationship(object entity,
}

private object SetHasManyRelationship(object entity,
PropertyInfo[] entityProperties,
RelationshipAttribute attr,
ContextEntity contextEntity,
PropertyInfo[] entityProperties,
RelationshipAttribute attr,
ContextEntity contextEntity,
Dictionary<string, RelationshipData> relationships)
{
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == attr.InternalRelationshipName);
Expand All @@ -179,7 +200,7 @@ private object SetHasManyRelationship(object entity,

var genericProcessor = _genericProcessorFactor.GetProcessor(attr.Type);
var ids = relationshipData.ManyData.Select(r => r["id"]);
genericProcessor.SetRelationships(entity, attr, ids);
genericProcessor.SetRelationships(entity, attr, ids);
}

return entity;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using JsonApiDotNetCore.Controllers;
using Microsoft.AspNetCore.Mvc;

namespace JsonApiDotNetCoreExample.Controllers.Restricted
{
[Route("[controller]")]
[HttpReadOnly]
public class ReadOnlyController : Controller
{
[HttpGet]
public IActionResult Get() => Ok();

[HttpPost]
public IActionResult Post() => Ok();

[HttpPatch]
public IActionResult Patch() => Ok();

[HttpDelete]
public IActionResult Delete() => Ok();
}

[Route("[controller]")]
[NoHttpPost]
public class NoHttpPostController : Controller
{
[HttpGet]
public IActionResult Get() => Ok();

[HttpPost]
public IActionResult Post() => Ok();

[HttpPatch]
public IActionResult Patch() => Ok();

[HttpDelete]
public IActionResult Delete() => Ok();
}

[Route("[controller]")]
[NoHttpPatch]
public class NoHttpPatchController : Controller
{
[HttpGet]
public IActionResult Get() => Ok();

[HttpPost]
public IActionResult Post() => Ok();

[HttpPatch]
public IActionResult Patch() => Ok();

[HttpDelete]
public IActionResult Delete() => Ok();
}

[Route("[controller]")]
[NoHttpDelete]
public class NoHttpDeleteController : Controller
{
[HttpGet]
public IActionResult Get() => Ok();

[HttpPost]
public IActionResult Post() => Ok();

[HttpPatch]
public IActionResult Patch() => Ok();

[HttpDelete]
public IActionResult Delete() => Ok();
}
}
Loading