Browse Source

Use ContentTypes for dataType and Accept in jQuery proxy generator

Refs abpframework/abp#23732
pull/25639/head
maliming 4 months ago
parent
commit
a8d93a245d
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 44
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs
  2. 129
      framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator_ContentTypes_Tests.cs

44
framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs

@ -136,9 +136,7 @@ public class JQueryProxyScriptGenerator : IProxyScriptGenerator, ITransientDepen
AddAjaxCallParameters(script, action);
var ajaxParamsIsFromForm = action.Parameters.Any(x => x.BindingSourceId == ParameterBindingSources.Form);
var dataType = action.ReturnValue.Type == ReturnValueApiDescriptionModel.Create(typeof(string)).Type
? "{ dataType: 'text' }, "
: string.Empty;
var dataType = GetJQueryDataTypeAndAcceptOverride(action);
script.AppendLine(ajaxParamsIsFromForm
? " }, $.extend(true, {}, " + dataType + "{ contentType: 'application/x-www-form-urlencoded; charset=UTF-8' }, ajaxParams)));"
: " }, " + dataType + "ajaxParams));");
@ -146,6 +144,46 @@ public class JQueryProxyScriptGenerator : IProxyScriptGenerator, ITransientDepen
script.AppendLine(" };");
}
private static string GetJQueryDataTypeAndAcceptOverride(ActionApiDescriptionModel action)
{
var contentTypes = action.ReturnValue.ContentTypes;
var isStringReturn = action.ReturnValue.Type == ReturnValueApiDescriptionModel.Create(typeof(string)).Type;
if (contentTypes is { Count: > 0 })
{
var normalized = contentTypes.Select(NormalizeMediaType).ToList();
if (normalized.Any(IsJsonMediaType))
{
return "{ dataType: 'json', headers: { Accept: 'application/json' } }, ";
}
if (normalized.All(ct => ct.StartsWith("text/", StringComparison.OrdinalIgnoreCase)))
{
return "{ dataType: 'text', headers: { Accept: 'text/plain' } }, ";
}
}
return isStringReturn ? "{ dataType: 'text' }, " : string.Empty;
}
private static bool IsJsonMediaType(string normalizedMediaType)
{
return normalizedMediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase) ||
normalizedMediaType.Equals("text/json", StringComparison.OrdinalIgnoreCase) ||
normalizedMediaType.EndsWith("+json", StringComparison.OrdinalIgnoreCase);
}
private static string NormalizeMediaType(string mediaType)
{
if (string.IsNullOrWhiteSpace(mediaType))
{
return string.Empty;
}
var semi = mediaType.IndexOf(';');
return (semi < 0 ? mediaType : mediaType.Substring(0, semi)).Trim();
}
private static string FindBestApiVersion(ActionApiDescriptionModel action)
{
//var configuredVersion = GetConfiguredApiVersion(); //TODO: Implement

129
framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator_ContentTypes_Tests.cs

@ -0,0 +1,129 @@
#nullable enable
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Http.ProxyScripting.Generators;
using Volo.Abp.Http.ProxyScripting.Generators.JQuery;
using Xunit;
namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery;
public class JQueryProxyScriptGenerator_ContentTypes_Tests
{
private readonly JQueryProxyScriptGenerator _generator = new(
Microsoft.Extensions.Options.Options.Create(new DynamicJavaScriptProxyOptions()));
[Fact]
public void Should_Emit_Json_DataType_And_Accept_When_ContentTypes_Contain_Json()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "System.String",
contentTypes: new[] { "text/plain", "application/json", "text/json" }));
script.ShouldContain("dataType: 'json'");
script.ShouldContain("Accept: 'application/json'");
script.ShouldNotContain("dataType: 'text'");
}
[Fact]
public void Should_Emit_Text_DataType_And_Accept_When_ContentTypes_Only_Text()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "System.String",
contentTypes: new[] { "text/plain", "text/csv" }));
script.ShouldContain("dataType: 'text'");
script.ShouldContain("Accept: 'text/plain'");
}
[Fact]
public void Should_Fallback_To_Legacy_Text_When_Return_Is_String_And_ContentTypes_Null()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "System.String",
contentTypes: null));
// Legacy behavior preserved: string return → dataType: 'text' (no Accept override)
script.ShouldContain("dataType: 'text'");
script.ShouldNotContain("Accept:");
}
[Fact]
public void Should_Emit_No_DataType_For_Non_String_Return_Without_ContentTypes()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "System.Int32",
contentTypes: null));
script.ShouldNotContain("dataType: 'text'");
script.ShouldNotContain("dataType: 'json'");
}
[Fact]
public void Should_Not_Emit_Json_DataType_When_Only_Binary_ContentTypes()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "System.Byte[]",
contentTypes: new[] { "application/octet-stream", "image/png" }));
// jQuery dataType doesn't have a clean "blob" — fall through to no override
script.ShouldNotContain("dataType: 'json'");
script.ShouldNotContain("dataType: 'text'");
}
[Fact]
public void Should_Prefer_Json_When_Json_Present_Even_With_Other_Types()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "My.Project.UserDto",
contentTypes: new[] { "application/xml", "application/json", "text/html" }));
script.ShouldContain("dataType: 'json'");
script.ShouldContain("Accept: 'application/json'");
}
[Fact]
public void Case_Insensitive_Json_Detection()
{
var script = _generator.CreateScript(BuildAppModel(
returnType: "System.String",
contentTypes: new[] { "APPLICATION/JSON" }));
script.ShouldContain("dataType: 'json'");
}
private static ApplicationApiDescriptionModel BuildAppModel(string returnType, IList<string>? contentTypes)
{
var model = ApplicationApiDescriptionModel.Create();
var module = model.GetOrAddModule("app", "Default");
var controller = module.GetOrAddController(
name: "TestController",
groupName: null,
isRemoteService: true,
isIntegrationService: false,
apiVersion: null,
type: typeof(object));
var action = new ActionApiDescriptionModel
{
UniqueName = "DoSomethingAsync",
Name = "DoSomethingAsync",
HttpMethod = "GET",
Url = "api/test/do-something",
SupportedVersions = new List<string>(),
ParametersOnMethod = new List<MethodParameterApiDescriptionModel>(),
Parameters = new List<ParameterApiDescriptionModel>(),
ReturnValue = new ReturnValueApiDescriptionModel
{
Type = returnType,
TypeSimple = returnType,
ContentTypes = contentTypes,
},
AuthorizeDatas = new List<AuthorizeDataApiDescriptionModel>(),
};
controller.AddAction("DoSomethingAsync", action);
return model;
}
}
Loading…
Cancel
Save